From 1f3c9d1f772a458f2090dac9cd4db1d0792e38dc Mon Sep 17 00:00:00 2001 From: Mintsuki Date: Sun, 13 Sep 2026 16:48:35 +0200 Subject: [PATCH 1/5] Fix INT_WAKE and IRQ_TIMER reading dev from an unrelocated address Signed-off-by: Mintsuki --- src/Kernel/KInterrupts.ZC | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Kernel/KInterrupts.ZC b/src/Kernel/KInterrupts.ZC index 2108971a6..c661b1235 100755 --- a/src/Kernel/KInterrupts.ZC +++ b/src/Kernel/KInterrupts.ZC @@ -5,9 +5,8 @@ INT_MP_CRASH_ADDR:: //Forward reference to work around compiler INT_WAKE:: PUSH RDX PUSH RAX - MOV EAX, &dev + MOV RAX, U64 [&dev.uncached_alias] MOV EDX, U32 LAPIC_EOI - MOV RAX, U64 CDevGlobals.uncached_alias[RAX] MOV U32 [RAX+RDX], 0 POP RAX POP RDX @@ -46,9 +45,8 @@ IRQ_TIMER:: //I_TIMER TEST RAX, RAX JZ @@20 - MOV EAX, &dev + MOV RAX, U64 [&dev.uncached_alias] MOV EDX, U32 LAPIC_EOI - MOV RAX, U64 CDevGlobals.uncached_alias[RAX] MOV U32 [RAX + RDX], 0 JMP @@25 From 02c831d15a739159c34ead75200d1f503a4dff68 Mon Sep 17 00:00:00 2001 From: Mintsuki Date: Sun, 13 Sep 2026 12:40:32 +0200 Subject: [PATCH 2/5] Pass the ACPI RSDP to the kernel and add ACPI table lookup Signed-off-by: Mintsuki --- src/Kernel/ACPI.ZC | 70 +++++++++++++++++++++++++++++++++++++ src/Kernel/KStart16.ZC | 2 ++ src/Kernel/Kernel.PRJ | 1 + src/Kernel/KernelA.HH | 29 +++++++++++++++ src/Kernel/KernelB.HH | 3 ++ src/Kernel/KernelC.HH | 4 +++ zealbooter/src/zealbooter.c | 11 ++++++ 7 files changed, 120 insertions(+) create mode 100755 src/Kernel/ACPI.ZC diff --git a/src/Kernel/ACPI.ZC b/src/Kernel/ACPI.ZC new file mode 100755 index 000000000..74026e3d9 --- /dev/null +++ b/src/Kernel/ACPI.ZC @@ -0,0 +1,70 @@ +//ACPI table lookup. +//Based on ACPI specification 6.6 + +CACPIRSDP *ACPIRSDPFind() +{//Find Root System Description Pointer. Return NULL if not found. + I64 i, ebda; + U8 noreg checksum, *mem; + + if (sys_acpi_rsdp) //passed by bootloader on UEFI systems, ACPI 6.6 section 5.2.5.2 + return sys_acpi_rsdp; + + //Search first KB of EBDA, then 0xE0000 - 0x100000, ACPI 6.6 section 5.2.5.1 + ebda = *0x40E(U16 *) << 4; + if (ebda < 0x80000 || ebda >= 0xA0000) + ebda = 0x80000; + + for (mem = ebda; mem < 0x100000; mem += 16) //anchor sits on 16-byte boundary + { + if (mem == ebda + 1024) + mem = 0xE0000; + if (!MemCompare(mem, "RSD PTR ", 8)) + { + checksum = 0; + for (i = 0; i < 20; i++) + checksum += mem[i]; + + if (checksum == 0) //valid checksums sum to zero + return mem; + } + } + + return NULL; +} + +CACPISDTHeader *ACPITableFind(U8 *signature, I64 index=0) +{//Find index'th table with signature. Return NULL if not found. +//Only RSDP checksum is checked, firmware often gets table checksums wrong. + CACPIRSDP *rsdp = ACPIRSDPFind; + CACPISDTHeader *root, *table; + I64 i, entry_size; + + if (!rsdp) + return NULL; + + if (rsdp->revision >= 2 && rsdp->xsdt_address) //XSDT must be used if present, ACPI 6.6 section 5.2.8 + { + root = rsdp->xsdt_address; + entry_size = sizeof(U64); + } + else + { + root = rsdp->rsdt_address; + entry_size = sizeof(U32); + } + if (!root || root->length < sizeof(CACPISDTHeader)) + return NULL; + + for (i = sizeof(CACPISDTHeader); i + entry_size <= root->length; i += entry_size) + { + if (entry_size == sizeof(U64)) + table = *(root(U8 *) + i)(U64 *); + else + table = *(root(U8 *) + i)(U32 *); + + if (table && !MemCompare(table->signature, signature, 4) && !index--) + return table; + } + + return NULL; +} diff --git a/src/Kernel/KStart16.ZC b/src/Kernel/KStart16.ZC index 13fef09e0..6552a1a6e 100755 --- a/src/Kernel/KStart16.ZC +++ b/src/Kernel/KStart16.ZC @@ -68,6 +68,8 @@ SYS_BOOTLOADER_ID:: DU8 BL_ZEAL; SYS_FRAMEBUFFER_LIST:: DU8 sizeof(CVideoInfo) * VBE_MODES_NUM DUP(0); +SYS_ACPI_RSDP:: DU64 0; + #assert $$ - SYS_KERNEL == sizeof(CKernel) - sizeof(CZXE) REQUESTED_SCREEN_WIDTH: DU16 1024; diff --git a/src/Kernel/Kernel.PRJ b/src/Kernel/Kernel.PRJ index 7dda5c80e..d04411768 100755 --- a/src/Kernel/Kernel.PRJ +++ b/src/Kernel/Kernel.PRJ @@ -52,6 +52,7 @@ #include "Job" #include "PCIBIOS" #include "PCI" +#include "ACPI" #include "MultiProc" #include "EdLite" #include "BlkDev/MakeBlkDev" diff --git a/src/Kernel/KernelA.HH b/src/Kernel/KernelA.HH index d9d731b23..fbaf99903 100755 --- a/src/Kernel/KernelA.HH +++ b/src/Kernel/KernelA.HH @@ -576,6 +576,7 @@ class CKernel Bool sys_is_uefi_booted; U8 sys_bootloader_id; CVideoInfo sys_framebuffer_list[VBE_MODES_NUM]; + U64 sys_acpi_rsdp; }; // Bootloader IDs @@ -2390,6 +2391,34 @@ class CUAsmGlobals mem_arg_mask; }; +#help_index "Devices/ACPI" +//ACPI table parsing based on ACPI specification 6.6 +class CACPIRSDP +{//ACPI 6.6 section 5.2.5.3 + U8 signature[8], // 'RSD PTR ' + checksum, + oem_id[6], + revision; + U32 rsdt_address, + length; + U64 xsdt_address; + U8 extended_checksum, + reserved[3]; +}; + +class CACPISDTHeader +{//ACPI 6.6 section 5.2.6 + U8 signature[4]; + U32 length; + U8 revision, + checksum, + oem_id[6], + oem_table_id[8]; + U32 oem_revision, + creator_id, + creator_revision; +}; + #help_index "Devices/SMBIOS" //SMBIOS parsing based on SMBIOS specification 3.3.0 (document "DSP0134") #define SMBIOSt_BIOS 0 diff --git a/src/Kernel/KernelB.HH b/src/Kernel/KernelB.HH index 7ccb0348c..4f17363c4 100755 --- a/src/Kernel/KernelB.HH +++ b/src/Kernel/KernelB.HH @@ -252,6 +252,9 @@ public _extern SYS_FRAMEBUFFER_BPP U8 sys_framebuffer_bpp; public U64 sys_framebuffer_pidth; _extern SYS_FRAMEBUFFER_LIST CVideoInfo sys_framebuffer_list[VBE_MODES_NUM]; +#help_index "Devices/ACPI" +public _extern SYS_ACPI_RSDP CACPIRSDP *sys_acpi_rsdp; + #help_index "Processor/SMBIOS" public _extern SYS_SMBIOS_ENTRY U8 *sys_smbios_entry; diff --git a/src/Kernel/KernelC.HH b/src/Kernel/KernelC.HH index 18c28999b..5bb18667b 100755 --- a/src/Kernel/KernelC.HH +++ b/src/Kernel/KernelC.HH @@ -1,3 +1,7 @@ +#help_index "ACPI" +extern CACPIRSDP *ACPIRSDPFind(); +extern CACPISDTHeader *ACPITableFind(U8 *signature, I64 index=0); + #help_index "AutoComplete" #help_file "::/Doc/AutoComplete" public extern CAutoCompleteGlobals ac; diff --git a/zealbooter/src/zealbooter.c b/zealbooter/src/zealbooter.c index 66fa857d6..8fcd80d41 100644 --- a/zealbooter/src/zealbooter.c +++ b/zealbooter/src/zealbooter.c @@ -40,6 +40,12 @@ static volatile struct limine_smbios_request smbios_request = { .revision = 0 }; +__attribute__((used, section(".limine_requests"))) +static volatile struct limine_rsdp_request rsdp_request = { + .id = LIMINE_RSDP_REQUEST_ID, + .revision = 0 +}; + __attribute__((used, section(".limine_requests"))) static volatile struct limine_efi_system_table_request efi_request = { .id = LIMINE_EFI_SYSTEM_TABLE_REQUEST_ID, @@ -137,6 +143,7 @@ struct CKernel { uint8_t sys_is_uefi_booted; uint8_t sys_bootloader_id; struct CVideoInfo sys_framebuffer_list[VBE_MODES_NUM]; + uint64_t sys_acpi_rsdp; } __attribute__((packed)); #define BL_ZEAL 0 @@ -348,6 +355,10 @@ void kmain(void) { kernel->sys_smbios_entry = (uintptr_t)sys_smbios_entry - hhdm_request.response->offset; } + if (rsdp_request.response != NULL) { + kernel->sys_acpi_rsdp = (uintptr_t)rsdp_request.response->address - hhdm_request.response->offset; + } + memcpy(kernel->sys_disk_uuid, &module_kernel->gpt_disk_uuid, sizeof(kernel->sys_disk_uuid)); void *const trampoline_phys = (void *)final_address + module_kernel->size; From 933732d1f76ee7f6b01efd793ff213d08eba5e0b Mon Sep 17 00:00:00 2001 From: Mintsuki Date: Tue, 3 Mar 2026 02:58:31 +0100 Subject: [PATCH 3/5] Add x2APIC support Signed-off-by: Mintsuki --- src/Demo/Lectures/PCIInterrupts.ZC | 11 +-- src/Demo/MultiCore/Interrupts.ZC | 4 +- src/Home/Net/Drivers/E1000.ZC | 2 +- src/Home/Net/Drivers/PCNet.ZC | 2 +- src/Home/Net/Drivers/RTL8139.ZC | 2 +- src/Home/Net/Utilities/NetHandler.ZC | 2 +- src/Kernel/KInterrupts.ZC | 34 ++++++- src/Kernel/KStart32.ZC | 1 + src/Kernel/KUtils.ZC | 15 ++++ src/Kernel/KernelA.HH | 39 +++++++- src/Kernel/KernelB.HH | 2 + src/Kernel/KernelC.HH | 1 + src/Kernel/MultiProc.ZC | 127 +++++++++++++++++++++------ 13 files changed, 202 insertions(+), 40 deletions(-) diff --git a/src/Demo/Lectures/PCIInterrupts.ZC b/src/Demo/Lectures/PCIInterrupts.ZC index d7e325338..f74e041e3 100755 --- a/src/Demo/Lectures/PCIInterrupts.ZC +++ b/src/Demo/Lectures/PCIInterrupts.ZC @@ -1,6 +1,7 @@ //See $LK,"PCI",A="HI:PCI"$. -#if mp_count > 1 +//I/O APIC destinations are 8-bit and 0xFF is broadcast +#if mp_count > 1 && dev.mp_apic_ids[1] < XAPIC_BROADCAST_ID #define DEST_CPU 1 #else #define DEST_CPU 0 @@ -11,25 +12,25 @@ interrupt U0 IntMy40() { lock {progress1++;} - *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; + LAPICEoi; } interrupt U0 IntMy41() { lock {progress2++;} - *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; + LAPICEoi; } interrupt U0 IntMy42() { lock {progress3++;} - *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; + LAPICEoi; } interrupt U0 IntMy43() { lock {progress4++;} - *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; + LAPICEoi; } I64 vect[4] = {&IntMy40, &IntMy41, &IntMy42, &IntMy43}; diff --git a/src/Demo/MultiCore/Interrupts.ZC b/src/Demo/MultiCore/Interrupts.ZC index 1705569e6..fadbdcdc9 100755 --- a/src/Demo/MultiCore/Interrupts.ZC +++ b/src/Demo/MultiCore/Interrupts.ZC @@ -1,12 +1,12 @@ interrupt U0 IntBeepOn() { - *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; //signal end of interrupt + LAPICEoi; //signal end of interrupt Sound(34); } interrupt U0 IntBeepOff() { - *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; + LAPICEoi; Sound; } diff --git a/src/Home/Net/Drivers/E1000.ZC b/src/Home/Net/Drivers/E1000.ZC index d39944e68..ed9078e52 100755 --- a/src/Home/Net/Drivers/E1000.ZC +++ b/src/Home/Net/Drivers/E1000.ZC @@ -421,7 +421,7 @@ interrupt U0 E1000IRQ() E1000MMIORead(E1000_REG_ICR); // clear pending interrupts? - *(dev.uncached_alias + LAPIC_EOI)(U32*) = 0; + LAPICEoi; } U0 PCIInterruptsReroute(I64 base) diff --git a/src/Home/Net/Drivers/PCNet.ZC b/src/Home/Net/Drivers/PCNet.ZC index 637956de8..550df51b2 100755 --- a/src/Home/Net/Drivers/PCNet.ZC +++ b/src/Home/Net/Drivers/PCNet.ZC @@ -697,7 +697,7 @@ interrupt U0 PCNetIRQ() "$$BG$$$$FG$$"); } - *(dev.uncached_alias + LAPIC_EOI)(U32*) = 0; + LAPICEoi; } U0 PCIInterruptsReroute(I64 base) diff --git a/src/Home/Net/Drivers/RTL8139.ZC b/src/Home/Net/Drivers/RTL8139.ZC index 6fa79bc07..a72bffa3c 100644 --- a/src/Home/Net/Drivers/RTL8139.ZC +++ b/src/Home/Net/Drivers/RTL8139.ZC @@ -388,7 +388,7 @@ interrupt U0 RTL8139IRQ() } } - *(dev.uncached_alias + LAPIC_EOI)(U32*) = 0; + LAPICEoi; } U0 PCIInterruptsReroute(I64 base) diff --git a/src/Home/Net/Utilities/NetHandler.ZC b/src/Home/Net/Utilities/NetHandler.ZC index f7dc20610..c43dfc61e 100755 --- a/src/Home/Net/Utilities/NetHandler.ZC +++ b/src/Home/Net/Utilities/NetHandler.ZC @@ -97,7 +97,7 @@ interrupt U0 NetHandler() "===================" "$$BG$$$$FG$$"); - *(dev.uncached_alias + LAPIC_EOI)(U32*) = 0; + LAPICEoi; } U0 NetHandlerInit() diff --git a/src/Kernel/KInterrupts.ZC b/src/Kernel/KInterrupts.ZC index c661b1235..64ea7ee46 100755 --- a/src/Kernel/KInterrupts.ZC +++ b/src/Kernel/KInterrupts.ZC @@ -5,9 +5,20 @@ INT_MP_CRASH_ADDR:: //Forward reference to work around compiler INT_WAKE:: PUSH RDX PUSH RAX - MOV RAX, U64 [&dev.uncached_alias] + PUSH RCX + CMP U64 [SYS_X2APIC], 0 + JE @@05 + MOV ECX, IA32_X2APIC_EOI + XOR EAX, EAX + XOR EDX, EDX + MFENCE //See $LK,"X2APICWrite",A="MN:X2APICWrite"$(). + LFENCE + WRMSR + JMP @@10 +@@05: MOV RAX, U64 [&dev.uncached_alias] MOV EDX, U32 LAPIC_EOI MOV U32 [RAX+RDX], 0 +@@10: POP RCX POP RAX POP RDX IRET @@ -45,7 +56,16 @@ IRQ_TIMER:: //I_TIMER TEST RAX, RAX JZ @@20 - MOV RAX, U64 [&dev.uncached_alias] + CMP U64 [SYS_X2APIC], 0 + JE @@17 + MOV ECX, IA32_X2APIC_EOI + XOR EAX, EAX + XOR EDX, EDX + MFENCE //See $LK,"X2APICWrite",A="MN:X2APICWrite"$(). + LFENCE + WRMSR + JMP @@25 +@@17: MOV RAX, U64 [&dev.uncached_alias] MOV EDX, U32 LAPIC_EOI MOV U32 [RAX + RDX], 0 JMP @@25 @@ -159,11 +179,19 @@ U0 IntPICInit() OutU8(PIC_2_DATA, 0xFF); } +public U0 LAPICEoi() +{//Signal end of interrupt to the local APIC. + if (sys_x2apic) + X2APICWrite(IA32_X2APIC_EOI, 0); + else + *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; +} + interrupt U0 IntNop() {//Make unplanned IRQs stop by all means! OutU8(PIC_2, PIC_EOI); OutU8(PIC_1, PIC_EOI); - *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; + LAPICEoi; } I64 IntEntryFree(I64 irq) diff --git a/src/Kernel/KStart32.ZC b/src/Kernel/KStart32.ZC index 4f296a0dd..d93b09210 100755 --- a/src/Kernel/KStart32.ZC +++ b/src/Kernel/KStart32.ZC @@ -69,6 +69,7 @@ SYS_CPU_STRUCTS:: DU64 0; SYS_MP_COUNT:: DU64 1; SYS_MP_COUNT_INITIAL:: DU64 1; SYS_MP_COUNT_LOCK:: DU64 1; +SYS_X2APIC:: DU64 0; ALIGN DEFAULT_CACHE_LINE_WIDTH, OC_NOP SYS_CACHE_LINE_WIDTH:: DU64 DEFAULT_CACHE_LINE_WIDTH; diff --git a/src/Kernel/KUtils.ZC b/src/Kernel/KUtils.ZC index be4d6bfc4..54b49267e 100755 --- a/src/Kernel/KUtils.ZC +++ b/src/Kernel/KUtils.ZC @@ -582,6 +582,21 @@ _MSR_WRITE:: POP RBP RET1 16 +/***********************************/ +_X2APIC_WRITE:: + PUSH RBP + MOV RBP, RSP + MOV RAX, SF_ARG2[RBP] + MOV RDX, RAX + SHR RDX, 32 + MOV RCX, SF_ARG1[RBP] +//x2APIC WRMSR isn't serializing, Intel SDM 325462-092 Vol. 3A section 13.12.3, AMD APM 40332 rev 4.10 Vol. 2 section 16.11.2 + MFENCE + LFENCE + WRMSR + POP RBP + RET1 16 + /***********************************/ _SYS_HLT:: CLI diff --git a/src/Kernel/KernelA.HH b/src/Kernel/KernelA.HH index fbaf99903..03b289ff5 100755 --- a/src/Kernel/KernelA.HH +++ b/src/Kernel/KernelA.HH @@ -638,6 +638,33 @@ class CKernel #define IA32_FS_BASE 0xC0000100 #define IA32_GS_BASE 0xC0000101 +#define IA32_APIC_BASEF_X2APIC (1 << 10) +#define IA32_APIC_BASEF_ENABLED (1 << 11) +#define CPUID_1_ECX_X2APIC (1 << 21) + +#define IA32_X2APIC_APICID 0x802 +#define IA32_X2APIC_VERSION 0x803 +#define IA32_X2APIC_TPR 0x808 +#define IA32_X2APIC_PPR 0x80A +#define IA32_X2APIC_EOI 0x80B +#define IA32_X2APIC_LDR 0x80D +#define IA32_X2APIC_SVR 0x80F +#define IA32_X2APIC_ISR0 0x810 +#define IA32_X2APIC_TMR0 0x818 +#define IA32_X2APIC_IRR0 0x820 +#define IA32_X2APIC_ESR 0x828 +#define IA32_X2APIC_ICR 0x830 +#define IA32_X2APIC_LVT_TIMER 0x832 +#define IA32_X2APIC_LVT_THERMAL 0x833 +#define IA32_X2APIC_LVT_PERF 0x834 +#define IA32_X2APIC_LVT_LINT0 0x835 +#define IA32_X2APIC_LVT_LINT1 0x836 +#define IA32_X2APIC_LVT_ERR 0x837 + +//Physical destination broadcast IDs, Intel SDM 325462-092 Vol. 3A sections 13.6.2.1 and 13.12.9 +#define XAPIC_BROADCAST_ID 0xFF +#define X2APIC_BROADCAST_ID 0xFFFFFFFF + //Programmable Interrupt Controller #define PIC_1 0x20 #define PIC_1_DATA 0x21 @@ -2419,6 +2446,16 @@ class CACPISDTHeader creator_revision; }; +class CACPIDMAR +{//Intel VT-d specification 5.20 section 8.1 + CACPISDTHeader header; + U8 host_address_width, + flags, + reserved[10]; +}; +#define DMARF_INTR_REMAP 1 +#define DMARF_X2APIC_OPT_OUT 2 //only valid with DMARF_INTR_REMAP + #help_index "Devices/SMBIOS" //SMBIOS parsing based on SMBIOS specification 3.3.0 (document "DSP0134") #define SMBIOSt_BIOS 0 @@ -3644,7 +3681,7 @@ public class CDevGlobals CIDTEntry *idt; U8 *mem64_ptr; U8 *uncached_alias; //Alias of lowest 4Gig. - U8 mp_apic_ids[MP_PROCESSORS_NUM]; + U32 mp_apic_ids[MP_PROCESSORS_NUM]; CMemRange mem32_head; CPCIDev pci_head; U64 user_int_bitmap; diff --git a/src/Kernel/KernelB.HH b/src/Kernel/KernelB.HH index 4f17363c4..c9434c3e2 100755 --- a/src/Kernel/KernelB.HH +++ b/src/Kernel/KernelB.HH @@ -191,6 +191,7 @@ public _extern SYS_CPU_STRUCTS CCPU *cpu_structs; //$LK,"Gs",A="MN:Gs"$ points t public _extern SYS_MP_COUNT I64 mp_count; //Count of cores _extern SYS_MP_COUNT_INITIAL I64 mp_count_initial; //only used during $LK,"Core0StartMP",A="MN:Core0StartMP"$ _extern SYS_MP_COUNT_LOCK I64 mp_count_lock; +public _extern SYS_X2APIC I64 sys_x2apic; _extern SYS_SEMAS CSema sys_semas[SEMA_SEMAS_NUM]; #help_index "MultiCore;Processor" @@ -222,6 +223,7 @@ public _extern _LXCHG_U8 U8 LXchgU8( U8 *dst, U8 d); //Locked eXchange U8s. public _extern _MSR_READ I64 MSRRead(I64 model_specific_reg); //Model Specific Reg See $LK,"MSRs",A="MN:IA32_EFER"$. public _extern _MSR_WRITE U0 MSRWrite(I64 model_specific_reg, I64 val); //Model Specific Reg See $LK,"MSRs",A="MN:IA32_EFER"$. public _extern _SYS_HLT U0 SysHlt(); //Loops doing HLT instruction. +public _extern _X2APIC_WRITE U0 X2APICWrite(I64 x2apic_reg, I64 val); //MSRWrite() for x2APIC regs, serializes first. public _extern _XCHG_I64 I64 XchgI64(I64 *dst, I64 d); //eXchange I64s. public _extern _XCHG_U16 U16 XchgU16(U16 *dst, U16 d); //eXchange U16s. public _extern _XCHG_U32 U32 XchgU32(U32 *dst, U32 d); //eXchange U32s. diff --git a/src/Kernel/KernelC.HH b/src/Kernel/KernelC.HH index 5bb18667b..6750279e6 100755 --- a/src/Kernel/KernelC.HH +++ b/src/Kernel/KernelC.HH @@ -644,6 +644,7 @@ public extern U8 *IntEntryGet(I64 irq); public extern U8 *IntEntrySet(I64 irq, U0 (*fp_new_handler)(), I64 type=IDTET_IRQ); public extern I64 IntEntryAlloc(); public extern I64 IntEntryFree(I64 irq); +public extern U0 LAPICEoi(); #help_index "ScreenCast;Cmd Line (Typically)" public extern Bool ScreenCast(Bool val=ON, Bool just_audio=FALSE, U8 *print_format="B:/Tmp/%X.GR"); diff --git a/src/Kernel/MultiProc.ZC b/src/Kernel/MultiProc.ZC index 9a7c7cf37..c267326ec 100755 --- a/src/Kernel/MultiProc.ZC +++ b/src/Kernel/MultiProc.ZC @@ -149,10 +149,15 @@ U0 MPInt(U8 num, I64 cpu_num=1) PUSHFD CLI //Multitasking safe because each core has a local apic and IRQs are off - while (*(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) & 0x1000) - PAUSE - *(dev.uncached_alias + LAPIC_ICR_HIGH)(U32 *) = dev.mp_apic_ids[cpu_num] << 24; - *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0x4000 + num; + if (sys_x2apic) + X2APICWrite(IA32_X2APIC_ICR, dev.mp_apic_ids[cpu_num] << 32 | 0x4000 | num); + else + { + while (*(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) & 0x1000) + PAUSE + *(dev.uncached_alias + LAPIC_ICR_HIGH)(U32 *) = dev.mp_apic_ids[cpu_num] << 24; + *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0x4000 + num; + } POPFD } @@ -160,15 +165,23 @@ U0 MPIntAll(U8 num) {//Generate interrupt for all but own core. PUSHFD CLI //Multitasking safe because each core has a local apic and IRQs are off - while (*(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) & 0x1000) - PAUSE - *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4800 + num; + if (sys_x2apic) + X2APICWrite(IA32_X2APIC_ICR, 0xC4800 + num); + else + { + while (*(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) & 0x1000) + PAUSE + *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4800 + num; + } POPFD } U0 MPNMInt() {//Generate nonmaskable interrupt. - *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4400; + if (sys_x2apic) + X2APICWrite(IA32_X2APIC_ICR, 0xC4400); + else + *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4400; } U0 MPHalt() @@ -178,18 +191,66 @@ U0 MPHalt() Busy(10000); } +U0 X2APICEnable() +{//Switch this core's local APIC to x2APIC mode. + I64 apic_base = MSRRead(IA32_LAPIC_BASE); + + if (apic_base & IA32_APIC_BASEF_X2APIC) + return; + if (!(apic_base & IA32_APIC_BASEF_ENABLED)) + {//Disabled to x2APIC mode is invalid, Intel SDM 325462-092 Vol. 3A section 13.12.5, AMD APM 40332 rev 4.10 Vol. 2 section 16.9.1 + apic_base |= IA32_APIC_BASEF_ENABLED; + MSRWrite(IA32_LAPIC_BASE, apic_base); + } + MSRWrite(IA32_LAPIC_BASE, apic_base | IA32_APIC_BASEF_X2APIC); +} + +U0 X2APICDetect() +{//Detect whether to use x2APIC mode. + CRAXRBXRCXRDX regs; + CACPIDMAR *dmar; + + if (MSRRead(IA32_LAPIC_BASE) & IA32_APIC_BASEF_X2APIC) + {//Firmware already enabled x2APIC + sys_x2apic = TRUE; + return; + } + + CPUId(1, ®s); + if (!(regs.rcx & CPUID_1_ECX_X2APIC)) + { + sys_x2apic = FALSE; + return; + } + + dmar = ACPITableFind("DMAR"); + if (dmar && dmar->flags & DMARF_INTR_REMAP && dmar->flags & DMARF_X2APIC_OPT_OUT) + {//Firmware opted out of x2APIC, Intel VT-d specification 5.20 section 8.1 + sys_x2apic = FALSE; + return; + } + + sys_x2apic = TRUE; +} + U0 MPAPICInit() {//Called by sys_task during start-up //and other cores during initialization //after $LK,"Core0StartMP",A="MN:Core0StartMP"$(). - *(dev.uncached_alias + LAPIC_SVR)(U32 *) |= LAPICF_APIC_ENABLED; - dev.mp_apic_ids[Gs->num] = *(dev.uncached_alias + LAPIC_APIC_ID)(U32 *) >> 24; - *(dev.uncached_alias + LAPIC_LDR)(U32 *) = dev.mp_apic_ids[Gs->num] << 24; - *(dev.uncached_alias + LAPIC_DFR)(U32 *) = 0xF0000000; - - // MemSet(dev.uncached_alias + LAPIC_IRR, 0, 0x20); - // MemSet(dev.uncached_alias + LAPIC_ISR, 0, 0x20); - // MemSet(dev.uncached_alias + LAPIC_TMR, 0, 0x20); + if (sys_x2apic) + { + X2APICEnable; + X2APICWrite(IA32_X2APIC_SVR, MSRRead(IA32_X2APIC_SVR) | LAPICF_APIC_ENABLED); + dev.mp_apic_ids[Gs->num] = MSRRead(IA32_X2APIC_APICID); + //DFR does not exist in x2APIC mode, LDR is read-only + } + else + { + *(dev.uncached_alias + LAPIC_SVR)(U32 *) |= LAPICF_APIC_ENABLED; + dev.mp_apic_ids[Gs->num] = *(dev.uncached_alias + LAPIC_APIC_ID)(U32 *) >> 24; + *(dev.uncached_alias + LAPIC_LDR)(U32 *) = dev.mp_apic_ids[Gs->num] << 24; + *(dev.uncached_alias + LAPIC_DFR)(U32 *) = 0xF0000000; + } RAXSet(Gs->tss->tr); LTR AX @@ -331,15 +392,30 @@ U0 Core0StartMP() mp_count_initial = mp_count = 1; mp_count_lock = 0; - *(dev.uncached_alias + LAPIC_LVT_ERR)(U32 *) = *(dev.uncached_alias + LAPIC_LVT_ERR)(U32 *) & 0xFFFFFF00 + MPN_VECT; - WBINVD //Not sure why this is needed. Might just need delay. $LK,"MemCopy",A="MN:MemCopy"$ above? + if (sys_x2apic) + { + X2APICWrite(IA32_X2APIC_LVT_ERR, MSRRead(IA32_X2APIC_LVT_ERR) & ~0x10FF | MPN_VECT); //Delivery status bit is read-only + WBINVD - *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4500; //assert init IPI - Busy(10000); + X2APICWrite(IA32_X2APIC_ICR, 0xC4500); //assert init IPI + Busy(10000); + + X2APICWrite(IA32_X2APIC_ICR, 0xC4600 + MPN_VECT); //start-up + Busy(200); + X2APICWrite(IA32_X2APIC_ICR, 0xC4600 + MPN_VECT); + } + else + { + *(dev.uncached_alias + LAPIC_LVT_ERR)(U32 *) = *(dev.uncached_alias + LAPIC_LVT_ERR)(U32 *) & 0xFFFFFF00 + MPN_VECT; + WBINVD //Not sure why this is needed. Might just need delay. $LK,"MemCopy",A="MN:MemCopy"$ above? - *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4600 + MPN_VECT; //start-up - Busy(200); - *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4600 + MPN_VECT; + *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4500; //assert init IPI + Busy(10000); + + *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4600 + MPN_VECT; //start-up + Busy(200); + *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4600 + MPN_VECT; + } Busy(100000); for (i = 0; i < 10000; i++) @@ -381,14 +457,15 @@ U0 Core0Init() //RAX has GS IMPORT SET_GS_BASE; CALL SET_GS_BASE - + + X2APICDetect; MPAPICInit; } interrupt U0 IntMPCrash() {//Entering the debugger from another core causes an interrupt on Core0 //Which calls this routine. - *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; + LAPICEoi; mp_count = 1; Raw(ON); text.raw_flags |= RAWF_SHOW_DOLLAR; From 347478fde2db00baee5d46871adb1ea40e0d3465 Mon Sep 17 00:00:00 2001 From: Mintsuki Date: Sun, 13 Sep 2026 12:45:28 +0200 Subject: [PATCH 4/5] Wait for the xAPIC ICR to be idle before sending any IPI Signed-off-by: Mintsuki --- src/Kernel/KernelA.HH | 1 + src/Kernel/MultiProc.ZC | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Kernel/KernelA.HH b/src/Kernel/KernelA.HH index 03b289ff5..5f4b86c93 100755 --- a/src/Kernel/KernelA.HH +++ b/src/Kernel/KernelA.HH @@ -693,6 +693,7 @@ class CKernel #define LAPIC_IRR (LAPIC_BASE + 0x200) #define LAPIC_ICR_LOW (LAPIC_BASE + 0x300) #define LAPIC_ICR_HIGH (LAPIC_BASE + 0x310) +#define LAPICF_ICR_SEND_PENDING 0x1000 #define LAPIC_LVT_TIMER (LAPIC_BASE + 0x320) #define LAPIC_LVT_THERMAL (LAPIC_BASE + 0x330) diff --git a/src/Kernel/MultiProc.ZC b/src/Kernel/MultiProc.ZC index c267326ec..b655df6c9 100755 --- a/src/Kernel/MultiProc.ZC +++ b/src/Kernel/MultiProc.ZC @@ -137,6 +137,18 @@ CCPU *CPUStructInit(I64 num, CCPU *c, CTask *executive_task) return c; } +U0 XAPICICRWait() +{//Wait for ICR delivery status to go idle, with a timeout. Call with IRQs off. + I64 i; + + for (i = 0; i < 1000000; i++) + { + if (!(*(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) & LAPICF_ICR_SEND_PENDING)) + return; + PAUSE + } +} + U0 MPInt(U8 num, I64 cpu_num=1) {//Generate interrupt for specified core. if (cpu_num >= mp_count) @@ -153,8 +165,7 @@ U0 MPInt(U8 num, I64 cpu_num=1) X2APICWrite(IA32_X2APIC_ICR, dev.mp_apic_ids[cpu_num] << 32 | 0x4000 | num); else { - while (*(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) & 0x1000) - PAUSE + XAPICICRWait; *(dev.uncached_alias + LAPIC_ICR_HIGH)(U32 *) = dev.mp_apic_ids[cpu_num] << 24; *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0x4000 + num; } @@ -169,8 +180,7 @@ U0 MPIntAll(U8 num) X2APICWrite(IA32_X2APIC_ICR, 0xC4800 + num); else { - while (*(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) & 0x1000) - PAUSE + XAPICICRWait; *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4800 + num; } POPFD @@ -178,10 +188,16 @@ U0 MPIntAll(U8 num) U0 MPNMInt() {//Generate nonmaskable interrupt. + PUSHFD + CLI //Multitasking safe because each core has a local apic and IRQs are off if (sys_x2apic) X2APICWrite(IA32_X2APIC_ICR, 0xC4400); else + { + XAPICICRWait; *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4400; + } + POPFD } U0 MPHalt() @@ -409,12 +425,16 @@ U0 Core0StartMP() *(dev.uncached_alias + LAPIC_LVT_ERR)(U32 *) = *(dev.uncached_alias + LAPIC_LVT_ERR)(U32 *) & 0xFFFFFF00 + MPN_VECT; WBINVD //Not sure why this is needed. Might just need delay. $LK,"MemCopy",A="MN:MemCopy"$ above? + XAPICICRWait; *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4500; //assert init IPI Busy(10000); + XAPICICRWait; *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4600 + MPN_VECT; //start-up Busy(200); + XAPICICRWait; *(dev.uncached_alias + LAPIC_ICR_LOW)(U32 *) = 0xC4600 + MPN_VECT; + XAPICICRWait; } Busy(100000); From dc726dee32672c1b8e5b57f7a533e83d5c25cf30 Mon Sep 17 00:00:00 2001 From: Mintsuki Date: Sun, 13 Sep 2026 12:45:42 +0200 Subject: [PATCH 5/5] Fix MSRRead() discarding the high 32 bits Signed-off-by: Mintsuki --- src/Kernel/KUtils.ZC | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Kernel/KUtils.ZC b/src/Kernel/KUtils.ZC index 54b49267e..29e154174 100755 --- a/src/Kernel/KUtils.ZC +++ b/src/Kernel/KUtils.ZC @@ -565,8 +565,7 @@ _MSR_READ:: MOV RCX, SF_ARG1[RBP] RDMSR SHL RDX, 32 - MOV EDX, EAX - MOV RAX, RDX + OR RAX, RDX POP RBP RET1 8