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

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

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

File size: 13.1 KB
Line 
1/*
2 * hal_apic.c - Advanced Programmable Interrupt Controller
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
18 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <hal_types.h>
23#include <hal_boot.h>
24#include <hal_register.h>
25#include <hal_segmentation.h>
26#include <hal_apic.h>
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
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
56uint64_t pit_ticks_base __in_kdata = 0;
57
58#define PIT_FREQUENCY   1193182
59#define HZ              100 /* 1/HZ = 10ms */
60#define muHZ            1000000 /* 1/muHZ = 1 microsecond */
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
82void hal_pit_reset(uint16_t val)
83{
84        pit_ticks_base = 0;
85
86        /* Initialize PIT clock 0 to value given. */
87        out8(PIT_CMD, CMD_COUNTER0|CMD_MODE2|CMD_16BIT);
88        out8(PIT_TIMER0, (val >> 0) & 0xFF);
89        out8(PIT_TIMER0, (val >> 8) & 0xFF);
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
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
137/* -------------------------------------------------------------------------- */
138
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
204void hal_com_send(uint8_t chan, char c)
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());
210        out8(RS232_COM1_BASE + RS232_DATA, chan | 0x80);
211        out8(RS232_COM1_BASE + RS232_DATA, c);
212
213        out8(RS232_COM1_BASE + RS232_MCR, mcr);
214}
215
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
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
258size_t ioapic_pins __in_kdata = 0;
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
268
269#define IOREDTBL        0x10
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
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
293void hal_ioapic_bind_irq(uint8_t irq, uint8_t vec, uint8_t dest)
294{
295        const uint64_t data = ((uint64_t)dest << IOREDTBL_DES_SHIFT) |
296            IOREDTBL_DEM_PHYS | IOREDTBL_DEL_FIXED | IOREDTBL_MSK | vec;
297
298        hal_ioapic_write(IOREDTBL + irq * 2, (uint32_t)(data & 0xFFFFFFFF));
299        hal_ioapic_write(IOREDTBL + irq * 2 + 1, (uint32_t)(data >> 32));
300}
301
302void hal_ioapic_enable_irq(uint8_t irq)
303{
304        uint32_t data[2];
305
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]);
313}
314
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
328static void hal_ioapic_init()
329{
330        uint32_t ver;
331        size_t i;
332
333        ver = hal_ioapic_read(IOAPICVER);
334        ioapic_pins = ((ver >> 16) & 0xFF) + 1;
335
336        /* Explicitly disable (mask) each vector */
337        for (i = 0; i < ioapic_pins; i++) {
338                hal_ioapic_disable_irq(i);
339        }
340
341        x86_printf("IOAPICPINS: #%z\n", ioapic_pins);
342
343        /* Now, enable the com1 port and the keyboard */
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);
348}
349
350/* -------------------------------------------------------------------------- */
351
352paddr_t lapic_pa __in_kdata = 0;
353vaddr_t lapic_va __in_kdata = 0;
354
355void hal_lapic_write(uint32_t reg, uint32_t val)
356{
357        *((volatile uint32_t *)((uint8_t *)lapic_va + reg)) = val;
358}
359
360uint32_t hal_lapic_read(uint32_t reg)
361{
362        return *((volatile uint32_t *)((uint8_t *)lapic_va + reg));
363}
364
365uint32_t hal_lapic_gid()
366{
367        return hal_lapic_read(LAPIC_ID) >> LAPIC_ID_SHIFT;
368}
369
370static void hal_lapic_icr_wait()
371{
372        while ((hal_lapic_read(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
373                pause();
374        }
375}
376
377/*
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 */
387        hal_lapic_write(LAPIC_ICR_TIMER, 0xFFFFFFFF);
388
389        /* Reset the PIT */
390        hal_pit_reset(-1);
391
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/*
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)
425 * Only the Spurious and APIC Timer interrupts are enabled.
426 */
427void cpu_lapic_init()
428{
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
434        hal_lapic_write(LAPIC_TPR, 0);
435        hal_lapic_write(LAPIC_EOI, 0);
436        hal_lapic_write(LAPIC_SVR, LAPIC_SVR_ENABLE|VECTOR_APIC_SPURIOU);
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);
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);
449        hal_lapic_calibrate();
450        hal_lapic_write(LAPIC_LVT_TMR, LAPIC_TMR_TM|LAPIC_TIMER_VECTOR);
451}
452
453/* -------------------------------------------------------------------------- */
454
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
493boot_cpuN(uint32_t gid, paddr_t pa)
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
521void hal_apic_init()
522{
523        /* Disable the PIC */
524        hal_pic_init();
525
526        /* Enable the IOAPIC */
527        hal_ioapic_init();
528
529        /* Enable the Serial Port */
530        hal_com_init();
531}
532
Note: See TracBrowser for help on using the repository browser.