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

Last change on this file since 520 was 482, checked in by viala@…, 6 years ago

[hal/x86_64] Add void type to function prototypes with no parameter

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