[metal] Enable & implement IRQ 0, in legacy 8259 mode

This commit is contained in:
tkchia 2023-09-13 15:53:17 +00:00
parent 97b8dd5d0a
commit 7dace0b171
12 changed files with 282 additions and 10 deletions

View file

@ -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 (;;)
;

View file

@ -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

View file

@ -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

View file

@ -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)

56
libc/irq/apic.c Normal file
View file

@ -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__ */

34
libc/irq/apic.internal.h Normal file
View file

@ -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_ */

67
libc/irq/irq-8259.c Normal file
View file

@ -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__ */

63
libc/irq/irq-init.S Normal file
View file

@ -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

12
libc/irq/irq.internal.h Normal file
View file

@ -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_ */

View file

@ -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];

View file

@ -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; \

View file

@ -38,6 +38,7 @@ LIBC_RUNTIME_A_DIRECTDEPS = \
LIBC_ELF \
LIBC_FMT \
LIBC_INTRIN \
LIBC_IRQ \
LIBC_NEXGEN32E \
LIBC_NT_ADVAPI32 \
LIBC_NT_KERNEL32 \