source: trunk/hal/x86_64/core/hal_apic.c @ 237

Last change on this file since 237 was 237, checked in by max@…, 7 years ago

Attach the secondary CPUs, and for now route the keyboard irq to
cpu1 - test only, but nice.

File size: 13.1 KB
RevLine 
[45]1/*
[82]2 * hal_apic.c - Advanced Programmable Interrupt Controller
[45]3 *
4 * Copyright (c) 2017 Maxime Villard
5 *
6 * This file is part of ALMOS-MKH.
7 *
8 * ALMOS-MKH is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2.0 of the License.
11 *
12 * ALMOS-MKH is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
[234]18 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
[45]19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <hal_types.h>
[83]23#include <hal_boot.h>
[82]24#include <hal_register.h>
[45]25#include <hal_segmentation.h>
[82]26#include <hal_apic.h>
[45]27#include <hal_internal.h>
28
29#include <memcpy.h>
30#include <thread.h>
31#include <string.h>
32#include <process.h>
33#include <printk.h>
34#include <vmm.h>
35#include <core.h>
36#include <cluster.h>
37
[82]38/* -------------------------------------------------------------------------- */
39
40#define PIC1_CMD        0x0020
41#define PIC1_DATA       0x0021
42#define PIC2_CMD        0x00a0
43#define PIC2_DATA       0x00a1
44
45static void hal_pic_init()
46{
47        /*
48         * Disable the PIC (8259A). We are going to use IOAPIC instead.
49         */
50        out8(PIC1_DATA, 0xff);
51        out8(PIC2_DATA, 0xff);
52}
53
54/* -------------------------------------------------------------------------- */
55
[117]56uint64_t pit_ticks_base __in_kdata = 0;
57
58#define PIT_FREQUENCY   1193182
59#define HZ              100 /* 1/HZ = 10ms */
[235]60#define muHZ            1000000 /* 1/muHZ = 1 microsecond */
[117]61
62#define PIT_TIMER0      0x40
63
64#define PIT_CMD         0x43
65#       define CMD_BINARY       0x00 /* Use Binary counter values */
66#       define CMD_BCD          0x01 /* Use Binary Coded Decimal counter values */
67#       define CMD_MODE0        0x00 /* Interrupt on Terminal Count */
68#       define CMD_MODE1        0x02 /* Hardware Retriggerable One-Shot */
69#       define CMD_MODE2        0x04 /* Rate Generator */
70#       define CMD_MODE3        0x06 /* Square Wave */
71#       define CMD_MODE4        0x08 /* Software Trigerred Strobe */
72#       define CMD_MODE5        0x0a /* Hardware Trigerred Strobe */
73#       define CMD_LATCH        0x00 /* latch counter for reading */
74#       define CMD_LSB          0x10 /* LSB, 8 bits */
75#       define CMD_MSB          0x20 /* MSB, 8 bits */
76#       define CMD_16BIT        0x30 /* LSB and MSB, 16 bits */
77#       define CMD_COUNTER0     0x00
78#       define CMD_COUNTER1     0x40
79#       define CMD_COUNTER2     0x80
80#       define CMD_READBACK     0xc0
81
[235]82void hal_pit_reset(uint16_t val)
[117]83{
[235]84        pit_ticks_base = 0;
85
86        /* Initialize PIT clock 0 to value given. */
[117]87        out8(PIT_CMD, CMD_COUNTER0|CMD_MODE2|CMD_16BIT);
[235]88        out8(PIT_TIMER0, (val >> 0) & 0xFF);
89        out8(PIT_TIMER0, (val >> 8) & 0xFF);
[117]90}
91
92uint64_t
93hal_pit_timer_read()
94{
95        static uint16_t last;
96
97        uint8_t lo, hi;
98        uint16_t ctr;
99        uint64_t ticks;
100
101        /* Read the current timer counter. */
102        out8(PIT_CMD, CMD_COUNTER0|CMD_LATCH);
103        lo = in8(PIT_TIMER0);
104        hi = in8(PIT_TIMER0);
105        ctr = (hi << 8) | lo;
106
107        /* If the counter has wrapped, assume we're into the next tick. */
108        if (ctr > last)
109                pit_ticks_base += 0xFFFF;
110        last = ctr;
111
112        ticks = pit_ticks_base + (0xFFFF - ctr);
113
114        return ticks;
115}
116
[235]117/*
118 * Wait approximately n microseconds.
119 */
120void
121hal_pit_delay(uint64_t n)
122{
123        uint64_t nticks;
124
125        nticks = 0;
126        hal_pit_reset(0);
127
128        while (n > 0) {
129                while (hal_pit_timer_read() - nticks < (PIT_FREQUENCY + muHZ/2) / muHZ) {
130                        /* Wait 1/muHZ sec = 1 microsecond */
131                }
132                nticks++;
133                n--;
134        }
135}
136
[117]137/* -------------------------------------------------------------------------- */
138
[152]139#define BAUDRATE        19200
140#define BAUDRATE_DIV    (115200 / BAUDRATE)
141
142#define RS232_COM1_BASE 0x3F8
143
144#define RS232_DATA      0x00
145#define RS232_IER       0x01
146#       define IER_RD           0x01
147#       define IER_TBE          0x02
148#       define IER_ER_BRK       0x04
149#       define IER_RS232IN      0x08
150#define RS232_DIVLO     0x00    /* when DLAB = 1 */
151#define RS232_DIVHI     0x01    /* when DLAB = 1 */
152#define RS232_IIR       0x02
153#define RS232_LCR       0x03
154#       define LCR_DATA5        0x00
155#       define LCR_DATA6        0x01
156#       define LCR_DATA7        0x02
157#       define LCR_DATA8        0x03
158#       define LCR_TWOSTOP      0x04
159#       define LCR_PARITY       0x08
160#       define LCR_EVEN         0x10
161#       define LCR_STICK        0x20
162#       define LCR_DLAB         0x80
163#define RS232_MCR       0x04
164#       define MCR_DTR          0x01
165#       define MCR_RTS          0x02
166#       define MCR_ELL          0x04
167#       define MCR_IR           0x40
168#define RS232_LSR       0x05
169#       define LSR_DR           0x01
170#       define LSR_OVR          0x02
171#       define LSR_PE           0x04
172#       define LSR_FE           0x08
173#       define LSR_BRK          0x10
174#       define LSR_TBE          0x20
175#       define LSR_TE           0x40
176#define RS232_MSR       0x06
177#       define MSR_DCTS         0x01
178#       define MSR_DDSR         0x02
179#       define MSR_DRI          0x04
180#       define MSR_DDCD         0x08
181#       define MSR_CTS          0x10
182#       define MSR_DSR          0x20
183#       define MSR_RI           0x40
184#       define MSR_DCD          0x80
185
186#define RS232_SCRATCH   0x07
187
188static bool_t hal_com_received()
189{
190        return (in8(RS232_COM1_BASE + RS232_LSR) & LSR_DR) != 0;
191}
192
193static bool_t hal_com_transmit_empty()
194{
195        return (in8(RS232_COM1_BASE + RS232_LSR) & LSR_TBE) != 0;
196}
197
198char hal_com_read()
199{
200        while (!hal_com_received());
201        return in8(RS232_COM1_BASE + RS232_DATA);
202}
203
[154]204void hal_com_send(uint8_t chan, char c)
[152]205{
206        uint8_t mcr = in8(RS232_COM1_BASE + RS232_MCR);
207        out8(RS232_COM1_BASE + RS232_MCR, mcr | MCR_RTS);
208
209        while (!hal_com_transmit_empty());
[154]210        out8(RS232_COM1_BASE + RS232_DATA, chan | 0x80);
[152]211        out8(RS232_COM1_BASE + RS232_DATA, c);
212
213        out8(RS232_COM1_BASE + RS232_MCR, mcr);
214}
215
[154]216/*
217 * Called early to provide x86-specific messages. Interrupts disabled.
218 */
219void hal_com_init_early()
220{
221        /* Disable all interrupts */
222        out8(RS232_COM1_BASE + RS232_IER, 0x00);
223
224        /* Set baudrate */
225        out8(RS232_COM1_BASE + RS232_LCR, LCR_DLAB);
226        out8(RS232_COM1_BASE + RS232_DIVLO, BAUDRATE_DIV);
227        out8(RS232_COM1_BASE + RS232_DIVHI, 0);
228
229        /* 8bits, no parity, one stop bit */
230        out8(RS232_COM1_BASE + RS232_LCR, LCR_DATA8);
231
232        /* DTR set, and also DSR */
233        out8(RS232_COM1_BASE + RS232_MCR, MCR_DTR|MCR_IR);
234        out8(RS232_COM1_BASE + RS232_MSR, MSR_DSR);
235}
236
[152]237static void hal_com_init()
238{
239        /* Disable all interrupts */
240        out8(RS232_COM1_BASE + RS232_IER, 0x00);
241
242        /* Set baudrate */
243        out8(RS232_COM1_BASE + RS232_LCR, LCR_DLAB);
244        out8(RS232_COM1_BASE + RS232_DIVLO, BAUDRATE_DIV);
245        out8(RS232_COM1_BASE + RS232_DIVHI, 0);
246
247        /* 8bits, no parity, one stop bit */
248        out8(RS232_COM1_BASE + RS232_LCR, LCR_DATA8);
249
250        /* Enable IRQs, DTR set, and also DSR */
251        out8(RS232_COM1_BASE + RS232_IER, IER_RD|IER_RS232IN);
252        out8(RS232_COM1_BASE + RS232_MCR, MCR_DTR|MCR_IR);
253        out8(RS232_COM1_BASE + RS232_MSR, MSR_DSR);
254}
255
256/* -------------------------------------------------------------------------- */
257
[135]258size_t ioapic_pins __in_kdata = 0;
[89]259paddr_t ioapic_pa __in_kdata = 0;
260vaddr_t ioapic_va __in_kdata = 0;
261
262#define IOREGSEL        0x00
263#define IOWIN   0x10
264
265#define IOAPICID        0x00
266#define IOAPICVER       0x01
267#define IOAPICARB       0x02
[137]268
[89]269#define IOREDTBL        0x10
[137]270#       define IOREDTBL_DEL_FIXED       0x000
271#       define IOREDTBL_DEL_LOPRI       0x100
272#       define IOREDTBL_DEL_SMI         0x200
273#       define IOREDTBL_DEL_NMI         0x400
274#       define IOREDTBL_DEL_INIT        0x500
275#       define IOREDTBL_DEL_EXTINT      0x700
276#       define IOREDTBL_DEM_PHYS        0x000
277#       define IOREDTBL_DEM_LOGIC       0x800
278#       define IOREDTBL_DES_SHIFT       56
279#       define IOREDTBL_MSK             0x10000
[89]280
281void hal_ioapic_write(uint8_t reg, uint32_t val)
282{
283        *((volatile uint32_t *)((uint8_t *)ioapic_va + IOREGSEL)) = reg;
284        *((volatile uint32_t *)((uint8_t *)ioapic_va + IOWIN)) = val;
285}
286
287uint32_t hal_ioapic_read(uint8_t reg)
288{
289        *((volatile uint32_t *)((uint8_t *)ioapic_va + IOREGSEL)) = reg;
290        return *((volatile uint32_t *)((uint8_t *)ioapic_va + IOWIN));
291}
292
[203]293void hal_ioapic_bind_irq(uint8_t irq, uint8_t vec, uint8_t dest)
[89]294{
[203]295        const uint64_t data = ((uint64_t)dest << IOREDTBL_DES_SHIFT) |
296            IOREDTBL_DEM_PHYS | IOREDTBL_DEL_FIXED | IOREDTBL_MSK | vec;
[137]297
[203]298        hal_ioapic_write(IOREDTBL + irq * 2, (uint32_t)(data & 0xFFFFFFFF));
299        hal_ioapic_write(IOREDTBL + irq * 2 + 1, (uint32_t)(data >> 32));
[89]300}
301
[203]302void hal_ioapic_enable_irq(uint8_t irq)
[137]303{
[203]304        uint32_t data[2];
[137]305
[203]306        data[0] = hal_ioapic_read(IOREDTBL + irq * 2);
307        data[1] = hal_ioapic_read(IOREDTBL + irq * 2 + 1);
308
309        data[0] &= ~IOREDTBL_MSK;
310
311        hal_ioapic_write(IOREDTBL + irq * 2, data[0]);
312        hal_ioapic_write(IOREDTBL + irq * 2 + 1, data[1]);
[137]313}
314
[203]315void hal_ioapic_disable_irq(uint8_t irq)
316{
317        uint32_t data[2];
318
319        data[0] = hal_ioapic_read(IOREDTBL + irq * 2);
320        data[1] = hal_ioapic_read(IOREDTBL + irq * 2 + 1);
321
322        data[0] |= IOREDTBL_MSK;
323
324        hal_ioapic_write(IOREDTBL + irq * 2, data[0]);
325        hal_ioapic_write(IOREDTBL + irq * 2 + 1, data[1]);
326}
327
[89]328static void hal_ioapic_init()
329{
330        uint32_t ver;
[135]331        size_t i;
[89]332
333        ver = hal_ioapic_read(IOAPICVER);
[135]334        ioapic_pins = ((ver >> 16) & 0xFF) + 1;
[89]335
336        /* Explicitly disable (mask) each vector */
[135]337        for (i = 0; i < ioapic_pins; i++) {
[203]338                hal_ioapic_disable_irq(i);
[89]339        }
340
[135]341        x86_printf("IOAPICPINS: #%z\n", ioapic_pins);
342
[152]343        /* Now, enable the com1 port and the keyboard */
[203]344        hal_ioapic_bind_irq(IRQ_COM1, IOAPIC_COM1_VECTOR, 0);
345        hal_ioapic_enable_irq(IRQ_COM1);
346        hal_ioapic_bind_irq(IRQ_KEYBOARD, IOAPIC_KEYBOARD_VECTOR, 0);
347        hal_ioapic_enable_irq(IRQ_KEYBOARD);
[89]348}
349
350/* -------------------------------------------------------------------------- */
351
[45]352paddr_t lapic_pa __in_kdata = 0;
353vaddr_t lapic_va __in_kdata = 0;
354
[46]355void hal_lapic_write(uint32_t reg, uint32_t val)
[45]356{
[82]357        *((volatile uint32_t *)((uint8_t *)lapic_va + reg)) = val;
[45]358}
359
[46]360uint32_t hal_lapic_read(uint32_t reg)
[45]361{
[82]362        return *((volatile uint32_t *)((uint8_t *)lapic_va + reg));
[45]363}
364
[46]365uint32_t hal_lapic_gid()
[45]366{
[46]367        return hal_lapic_read(LAPIC_ID) >> LAPIC_ID_SHIFT;
368}
[45]369
[235]370static void hal_lapic_icr_wait()
371{
372        while ((hal_lapic_read(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
373                pause();
374        }
375}
376
[82]377/*
[117]378 * Use the PIT, which has a standard clock frequency, to determine the CPU's
379 * exact bus frequency.
380 */
381static void hal_lapic_calibrate()
382{
383        uint64_t pittick, lapictick0, lapictick1;
384        uint32_t lapicticks, lapicstart;
385
386        /* Initialize the LAPIC timer to the maximum value */
[145]387        hal_lapic_write(LAPIC_ICR_TIMER, 0xFFFFFFFF);
[117]388
[235]389        /* Reset the PIT */
390        hal_pit_reset(-1);
[135]391
[117]392        pittick = hal_pit_timer_read() + 1;
393        while (hal_pit_timer_read() < pittick) {
394                /* Wait until start of a PIT tick */
395        }
396
397        /* Read base count from LAPIC */
398        lapictick0 = hal_lapic_read(LAPIC_CCR_TIMER);
399
400        while (hal_pit_timer_read() < pittick + (PIT_FREQUENCY + HZ/2) / HZ) {
401                /* Wait 1/HZ sec = 10ms */
402        }
403
404        /* Read final count from LAPIC */
405        lapictick1 = hal_lapic_read(LAPIC_CCR_TIMER);
406
407        /* Total number of LAPIC ticks per 1/HZ tick */
408        lapicticks = (lapictick1 - lapictick0);
409
410        /* Finally, calibrate the timer, an interrupt each 1s. */
411        lapicstart = - (lapicticks * 100);
412        hal_lapic_write(LAPIC_ICR_TIMER, lapicstart);
413}
414
415/*
[82]416 * We have 8 interrupt sources:
417 *  - Spurious
418 *  - APIC Timer (TMR)
419 *  - Local Interrupt 0 (LINT0)
420 *  - Local Interrupt 1 (LINT1)
421 *  - Performance Monitor Counters (PMC)
422 *  - Thermal Sensors (THM)
423 *  - APIC internal error (ERR)
424 *  - Extended (Implementation dependent)
[135]425 * Only the Spurious and APIC Timer interrupts are enabled.
[82]426 */
[237]427void cpu_lapic_init()
[46]428{
[82]429        if ((rdmsr(MSR_APICBASE) & APICBASE_PHYSADDR) != lapic_pa) {
430                x86_panic("APICBASE and ACPI don't match!\n");
431        }
432        wrmsr(MSR_APICBASE, lapic_pa | APICBASE_EN);
433
[46]434        hal_lapic_write(LAPIC_TPR, 0);
[82]435        hal_lapic_write(LAPIC_EOI, 0);
[138]436        hal_lapic_write(LAPIC_SVR, LAPIC_SVR_ENABLE|VECTOR_APIC_SPURIOU);
[82]437
438        /* Explicitly disable (mask) each vector */
439        hal_lapic_write(LAPIC_LVT_TMR, LAPIC_TMR_M);
440        hal_lapic_write(LAPIC_LVT_LINT0, LAPIC_LINT_M);
441        hal_lapic_write(LAPIC_LVT_LINT1, LAPIC_LINT_M);
442        hal_lapic_write(LAPIC_LVT_PMC, LAPIC_PMC_M);
443        hal_lapic_write(LAPIC_LVT_THM, LAPIC_THM_M);
444        hal_lapic_write(LAPIC_LVT_ERR, LAPIC_ERR_M);
[86]445
446        /* Now, enable the timer in repeated mode. */
447        hal_lapic_write(LAPIC_LVT_TMR, LAPIC_TMR_TM|LAPIC_TMR_M);
448        hal_lapic_write(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
[117]449        hal_lapic_calibrate();
[86]450        hal_lapic_write(LAPIC_LVT_TMR, LAPIC_TMR_TM|LAPIC_TIMER_VECTOR);
[45]451}
452
[82]453/* -------------------------------------------------------------------------- */
454
[235]455static void
456hal_ipi_init(uint32_t gid)
457{
458        /* clear the error status */
459        hal_lapic_write(LAPIC_ESR, 0);
460        (void)hal_lapic_read(LAPIC_ESR);
461
462        /* send the IPI */
463        hal_lapic_write(LAPIC_ICRHI, gid << LAPIC_ID_SHIFT);
464        hal_lapic_write(LAPIC_ICRLO, LAPIC_DLMODE_INIT | LAPIC_LEVEL_ASSERT);
465        hal_lapic_icr_wait();
466
467        /* wait 10ms */
468        hal_pit_delay(10000);
469
470        hal_lapic_write(LAPIC_ICRLO,
471            LAPIC_DLMODE_INIT | LAPIC_TRIGGER_LEVEL | LAPIC_LEVEL_DEASSERT);
472        hal_lapic_icr_wait();
473}
474
475static void
476hal_ipi_startup(uint32_t gid, paddr_t pa)
477{
478        /* clear the error status */
479        hal_lapic_write(LAPIC_ESR, 0);
480        (void)hal_lapic_read(LAPIC_ESR);
481
482        /* send the IPI */
483        hal_lapic_icr_wait();
484        hal_lapic_write(LAPIC_ICRHI, gid << LAPIC_ID_SHIFT);
485        hal_lapic_write(LAPIC_ICRLO, pa | LAPIC_DLMODE_STARTUP |
486            LAPIC_LEVEL_ASSERT);
487        hal_lapic_icr_wait();
488}
489
490/* -------------------------------------------------------------------------- */
491
492int
[236]493boot_cpuN(uint32_t gid, paddr_t pa)
[235]494{
495        /*
496         * Bootstrap code must be addressable in real mode and it must be
497         * page-aligned.
498         */
499        XASSERT(pa < 0x10000 && pa % PAGE_SIZE == 0);
500
501        /*
502         * Local cache flush, in case the BIOS has left the AP with its cache
503         * disabled. It may not be able to cope with MP coherency.
504         */
505        wbinvd();
506
507        hal_ipi_init(gid);
508        hal_pit_delay(10000);
509
510        hal_ipi_startup(gid, pa / PAGE_SIZE);
511        hal_pit_delay(200);
512
513        hal_ipi_startup(gid, pa / PAGE_SIZE);
514        hal_pit_delay(200);
515
516        return 0;
517}
518
519/* -------------------------------------------------------------------------- */
520
[82]521void hal_apic_init()
522{
523        /* Disable the PIC */
524        hal_pic_init();
525
[89]526        /* Enable the IOAPIC */
527        hal_ioapic_init();
[152]528
529        /* Enable the Serial Port */
530        hal_com_init();
[82]531}
532
Note: See TracBrowser for help on using the repository browser.