From 7dace0b1717e48500c9764c5f69d4f7f77973d0d Mon Sep 17 00:00:00 2001 From: tkchia Date: Wed, 13 Sep 2023 15:53:17 +0000 Subject: [PATCH] [metal] Enable & implement IRQ 0, in legacy 8259 mode --- examples/vga2.c | 9 +++-- libc/intrin/interrupts.S | 9 +---- libc/irq/acpi-madt-init.S | 10 ++++++ libc/irq/acpi.internal.h | 5 +++ libc/irq/apic.c | 56 +++++++++++++++++++++++++++++++ libc/irq/apic.internal.h | 34 +++++++++++++++++++ libc/irq/irq-8259.c | 67 +++++++++++++++++++++++++++++++++++++ libc/irq/irq-init.S | 63 ++++++++++++++++++++++++++++++++++ libc/irq/irq.internal.h | 12 +++++++ libc/runtime/efimain.greg.c | 3 ++ libc/runtime/pc.internal.h | 23 +++++++++++++ libc/runtime/runtime.mk | 1 + 12 files changed, 282 insertions(+), 10 deletions(-) create mode 100644 libc/irq/apic.c create mode 100644 libc/irq/apic.internal.h create mode 100644 libc/irq/irq-8259.c create mode 100644 libc/irq/irq-init.S create mode 100644 libc/irq/irq.internal.h diff --git a/examples/vga2.c b/examples/vga2.c index 0494796ee..83930bb98 100644 --- a/examples/vga2.c +++ b/examples/vga2.c @@ -26,8 +26,7 @@ __static_yoink("vga_console"); __static_yoink("_idt"); -__static_yoink("_AcpiMadtFlags"); -__static_yoink("_AcpiBootFlags"); +__static_yoink("_irq"); __static_yoink("EfiMain"); int main(int argc, char *argv[]) { @@ -38,6 +37,12 @@ int main(int argc, char *argv[]) { for (i = 0; i < argc; ++i) { printf("argv[%d] = \"%s\"\n", i, argv[i]); } + for (i = 1; i <= 50; ++i) { + printf("%d ", i); + fflush(stdout); + asm volatile("hlt"); + } + printf("\n"); printf("\e[92;44mHello World!\e[0m %d\n", 1 / (x + y - 3)); for (;;) ; diff --git a/libc/intrin/interrupts.S b/libc/intrin/interrupts.S index 557ca2b2c..0f0d7cc6d 100644 --- a/libc/intrin/interrupts.S +++ b/libc/intrin/interrupts.S @@ -26,6 +26,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" #include "libc/intrin/kprintf.h" +#include "libc/irq/irq.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/pc.internal.h" @@ -33,14 +34,6 @@ #define ISR_STK_SZ 0x10000 #define ISR_STK_ALIGN 0x10 -// Interrupt stack to use for IRQs. TODO. -#define IRQ_IST 1 -// Interrupt stack to use for CPU exceptions. -#define EXCEP_IST 2 - -// Interrupt numbers to use for IRQs 0 & 8. TODO: implement these! -#define IRQ0 0x20 -#define IRQ8 0x28 .init.start 100,_init_isr push %rdi diff --git a/libc/irq/acpi-madt-init.S b/libc/irq/acpi-madt-init.S index 04c506a42..33fbbf08d 100644 --- a/libc/irq/acpi-madt-init.S +++ b/libc/irq/acpi-madt-init.S @@ -49,3 +49,13 @@ _AcpiIoApics: .skip 8 .endobj _AcpiIoApics,globl .previous + .rodata + /* Yoinking _acpi from another module will link in ACPI support... */ +_acpi: + .endobj _acpi,globl + .previous + /* + * ...& also link in additional code needed for switching to legacy + * BIOS-style interrupt handling. + */ + .yoink _ApicDisableAll diff --git a/libc/irq/acpi.internal.h b/libc/irq/acpi.internal.h index f8175953d..c3698c339 100644 --- a/libc/irq/acpi.internal.h +++ b/libc/irq/acpi.internal.h @@ -5,6 +5,7 @@ #include "libc/log/color.internal.h" /** + * @internal * AcpiStatus values. */ #define kAcpiOk 0x0000 @@ -13,11 +14,13 @@ #define kAcpiExBadChecksum 0x2003 /** + * @internal * Flags for AcpiTableMadt::Flags. */ #define kAcpiMadtPcAtCompat 0x0001 /** + * @internal * Flags for AcpiTableFadt::BootFlags. */ #define kAcpiFadtLegacyDevices 0x0001 @@ -28,6 +31,7 @@ #define kAcpiFadtNoCmosRtc 0x0020 /** + * @internal * Values for AcpiSubtableHeader::Type under an AcpiTableMadt. */ #define kAcpiMadtLocalApic 0 @@ -37,6 +41,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) /** + * @internal * @fileoverview Declarations for bare metal code to interact with ACPI * * @see UEFI Forum, Inc. Advanced Configuration and Power Interface (ACPI) diff --git a/libc/irq/apic.c b/libc/irq/apic.c new file mode 100644 index 000000000..e344212c5 --- /dev/null +++ b/libc/irq/apic.c @@ -0,0 +1,56 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/inttypes.h" +#include "libc/irq/acpi.internal.h" +#include "libc/irq/apic.internal.h" + +#ifdef __x86_64__ + +/** + * @internal + * Disables all I/O APIC interrupts. + */ +textstartup void _ApicDisableAll(void) { + const AcpiMadtIoApic **icp = _AcpiIoApics, *ic; + HwIoApic *hwic; + size_t n = _AcpiNumIoApics; + unsigned intin; + uint32_t conf; + while (n-- != 0) { + ic = *icp++; + hwic = _AcpiOsMapUncachedMemory((uintptr_t)ic->Address, sizeof(HwIoApic)); + ACPI_INFO("stopping I/O APIC @ %p", hwic); + for (intin = 0; intin < 24; ++intin) { + hwic->rIoRegSel = IoApicRedTblLo(intin); + conf = hwic->rIoWin; + hwic->rIoWin = conf | kIoApicRedTblMasked; + } + } +} + +#endif /* __x86_64__ */ diff --git a/libc/irq/apic.internal.h b/libc/irq/apic.internal.h new file mode 100644 index 000000000..c35533c31 --- /dev/null +++ b/libc/irq/apic.internal.h @@ -0,0 +1,34 @@ +#ifndef COSMOPOLITAN_LIBC_IRQ_APIC_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_IRQ_APIC_INTERNAL_H_ + +/** + * @internal + * @fileoverview Declarations for bare metal code to interact with I/O APICs + * + * @see Intel Corporation. 82093AA I/O Advanced Programmable Interrupt + * Controller (IOAPIC), 1996. Intel order number 290566-001. + */ + +#define IoApicRedTblLo(INTIN) (0x10 + 2 * (INTIN)) +#define IoApicRedTblHi(INTIN) (0x11 + 2 * (INTIN)) +#define kIoApicRedTblMasked (1 << 16) + +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +COSMOPOLITAN_C_START_ + +/** + * @internal + * Structure of memory-mapped registers for an I/O APIC. + */ +typedef struct { + volatile uint32_t rIoRegSel; /* register select (index) */ + volatile uint32_t :32, :32, :32; + volatile uint32_t rIoWin; /* register data */ +} HwIoApic; + +extern void _ApicDisableAll(void); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_IRQ_APIC_INTERNAL_H_ */ diff --git a/libc/irq/irq-8259.c b/libc/irq/irq-8259.c new file mode 100644 index 000000000..6e0a231a1 --- /dev/null +++ b/libc/irq/irq-8259.c @@ -0,0 +1,67 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/intrin/weaken.h" +#include "libc/inttypes.h" +#include "libc/irq/acpi.internal.h" +#include "libc/irq/apic.internal.h" +#include "libc/irq/irq.internal.h" +#include "libc/runtime/pc.internal.h" + +#ifdef __x86_64__ + +textstartup void _Irq8259Init(void) { + if (_weaken(_ApicDisableAll)) _weaken(_ApicDisableAll)(); + ACPI_INFO("starting 8259 IRQs"); + outb(PIC1_CMD, PIC_INIT | PIC_IC4); /* ICW1 */ + outb(PIC2_CMD, PIC_INIT | PIC_IC4); + outb(PIC1_DATA, IRQ0); /* ICW2 */ + outb(PIC2_DATA, IRQ8); + outb(PIC1_DATA, 1 << 2); /* ICW3 — connect master IRQ 2 to */ + outb(PIC2_DATA, 1 << 1); /* (slave) IRQ 9 */ + outb(PIC1_DATA, PIC_UPM); /* ICW4 */ + outb(PIC2_DATA, PIC_UPM); + /* Set IRQ masks. */ + outb(PIC1_DATA, ~(1 << 0)); /* OCW1 — allow IRQ 0 on PIC 1 */ + outb(PIC2_DATA, ~0); + /* Send EOIs for good measure. */ + outb(PIC1_CMD, PIC_EOI); + outb(PIC2_CMD, PIC_EOI); + enable(); +} + +textstartup void _IrqHwInit(void) { + if (!_weaken(_AcpiMadtFlags) || + (_AcpiMadtFlags & kAcpiMadtPcAtCompat) != 0) { + _Irq8259Init(); + } else { + /* TODO */ + ACPI_FATAL("machine does not support 8259 IRQ handling"); + } +} + +#endif /* __x86_64__ */ diff --git a/libc/irq/irq-init.S b/libc/irq/irq-init.S new file mode 100644 index 000000000..fe140fadf --- /dev/null +++ b/libc/irq/irq-init.S @@ -0,0 +1,63 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/irq/irq.internal.h" +#include "libc/macros.internal.h" +#include "libc/runtime/pc.internal.h" + + .init.start 320,_init_irq + testb IsMetal() + jz 9f + push %rdi + push %rsi + ezlea _idt+IRQ0*0x10,di # fill IDT entry for IRQ 0 + ezlea _irq0_isr,dx + mov %edx,%eax + stosw + mov %cs,%eax + stosw + mov %rdx,%rax + mov $0b1000111000000000|IRQ_IST,%ax + stosl + shr $32,%rax + stosq + call _IrqHwInit # bring up interrupt controller(s) & + # enable interrupts + pop %rsi + pop %rdi +9: + .init.end 320,_init_irq +_irq0_isr: + push %rax + mov $PIC_EOI,%al + out %al,$PIC1_CMD + pop %rax + iretq + .rodata +_irq: + .endobj _irq,globl + .previous diff --git a/libc/irq/irq.internal.h b/libc/irq/irq.internal.h new file mode 100644 index 000000000..579b94203 --- /dev/null +++ b/libc/irq/irq.internal.h @@ -0,0 +1,12 @@ +#ifndef COSMOPOLITAN_LIBC_IRQ_IRQ_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_IRQ_IRQ_INTERNAL_H_ + +/** + * @internal + * Interrupt numbers to use for legacy BIOS style IRQs 0 & 8. + * TODO: implement these! + */ +#define IRQ0 0x20 +#define IRQ8 0x28 + +#endif /* COSMOPOLITAN_LIBC_IRQ_IRQ_INTERNAL_H_ */ diff --git a/libc/runtime/efimain.greg.c b/libc/runtime/efimain.greg.c index cee4fcff0..607c4c2c4 100644 --- a/libc/runtime/efimain.greg.c +++ b/libc/runtime/efimain.greg.c @@ -1,3 +1,4 @@ + /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ @@ -36,6 +37,8 @@ /* TODO: Why can't we change CR3? Could it really need PML5T? */ /* TODO: Why does QEMU in UEFI mode take ten seconds to boot? */ +__static_yoink("_acpi"); + struct EfiArgs { char *Args[0x400 / sizeof(char *)]; char ArgBlock[0xC00]; diff --git a/libc/runtime/pc.internal.h b/libc/runtime/pc.internal.h index 3089c7474..3f77d1240 100644 --- a/libc/runtime/pc.internal.h +++ b/libc/runtime/pc.internal.h @@ -122,6 +122,18 @@ #define GDT_LONG_DATA 48 #define GDT_LONG_TSS 56 +#define IRQ_IST 1 /* interrupt stack table (IST) entry to use for IRQs */ +#define EXCEP_IST 2 /* IST entry to use for CPU exceptions */ + +/* + * @see Intel Corporation. 8259A Programmable Interrupt Controller (8259A/ + * 8259A-2). 1988. Intel order number 231468-003. + * @see IBM. Technical Reference for the IBM Personal Computer AT. 1985, + * Section 5: System BIOS, p. 5-34. https://archive.org/details/ + * IBMPCATIBM5170TechnicalReference6280070SEP85/page/n205/mode/2up . + * @see Intel Corporation. 82371FB (PIIX) and 82371SB (PIIX3) PCI ISA IDE + * Xcelerator. 1997. Intel order number 290550-002. + */ #define PIC1 0x20 /* IO base address for master PIC */ #define PIC2 0xA0 /* IO base address for slave PIC */ #define PIC1_CMD PIC1 @@ -129,6 +141,9 @@ #define PIC2_CMD PIC2 #define PIC2_DATA (PIC2 + 1) #define PIC_EOI 0x20 /* End-of-interrupt command code */ +#define PIC_INIT 0x10 /* ICW1 initialize */ +#define PIC_IC4 0x01 /* ICW1 initialize needs ICW4 */ +#define PIC_UPM 0x01 /* ICW4 microprocessor mode (8086/8088 vs. 8085) */ #define PIC_READ_IRR 0x0a /* OCW3 irq ready next CMD read */ #define PIC_READ_ISR 0x0b /* OCW3 irq service next CMD read */ @@ -223,6 +238,14 @@ forceinline void outb(unsigned short port, unsigned char byte) { : "a"(byte), "dN"(port)); } +forceinline void disable(void) { + asm volatile("cli" : : : "memory"); +} + +forceinline void enable(void) { + asm volatile("sti" : : : "memory"); +} + #define __clear_page(page) \ ({ \ long di, cx; \ diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index 79652e598..fbb47b669 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -38,6 +38,7 @@ LIBC_RUNTIME_A_DIRECTDEPS = \ LIBC_ELF \ LIBC_FMT \ LIBC_INTRIN \ + LIBC_IRQ \ LIBC_NEXGEN32E \ LIBC_NT_ADVAPI32 \ LIBC_NT_KERNEL32 \