From 3b6e68bc16e85b0af2e1f4b265b78c387c870e0c Mon Sep 17 00:00:00 2001 From: kaguya Date: Fri, 24 Apr 2026 01:59:33 -0400 Subject: [PATCH] sys: Implement LAPIC and IOAPIC support, add PS/2 keyboard driver - Added LAPIC implementation in `apic.c` and `apic.h`, including initialization and basic operations. - Introduced IOAPIC (I/O Advanced Programmable Interrupt Controller) support in `ioapic.c` and `ioapic.h`, parsing the ACPI MADT to locate IOAPICs and manage redirection entries. - Implemented PS/2 keyboard driver in `ps2.c` and `ps2.h`, handling keyboard interrupts and translating scancodes to ASCII. - Attempted to make usermode work again (it failed, coming soon) - Cleaned up comments in `time.c` and `render.c` for clarity. Signed-off-by: kaguya --- ext2_root/init.elf | Bin 5104 -> 5104 bytes src/arch/x86_64/apic.c | 147 +++++++++++++++ src/arch/x86_64/apic.h | 77 ++++++++ src/arch/x86_64/ata.c | 2 +- src/arch/x86_64/gdt.c | 41 +++- src/arch/x86_64/i8259.h | 3 +- src/arch/x86_64/ioapic.c | 359 ++++++++++++++++++++++++++++++++++++ src/arch/x86_64/ioapic.h | 76 ++++++++ src/arch/x86_64/irq.c | 42 ++++- src/arch/x86_64/irq.h | 6 +- src/arch/x86_64/pci.h | 2 +- src/arch/x86_64/pit.c | 8 +- src/arch/x86_64/ps2.c | 332 --------------------------------- src/arch/x86_64/ps2.h | 29 --- src/arch/x86_64/usermode.c | 68 ++++--- src/input/input.c | 7 +- src/input/ps2.c | 193 +++++++++++++++++++ src/input/ps2.h | 9 + src/main.c | 85 ++------- src/syscall/syscall.c | 2 +- src/syscall/syscall_entry.S | 6 +- src/time/time.c | 1 - src/video/render.c | 2 +- user/build/crt0.o | Bin 928 -> 784 bytes user/build/init.elf | Bin 5104 -> 5104 bytes user/build/init.o | Bin 1424 -> 1464 bytes user/crt0.S | 16 +- 27 files changed, 1009 insertions(+), 504 deletions(-) create mode 100644 src/arch/x86_64/apic.c create mode 100644 src/arch/x86_64/apic.h create mode 100644 src/arch/x86_64/ioapic.c create mode 100644 src/arch/x86_64/ioapic.h delete mode 100644 src/arch/x86_64/ps2.c delete mode 100644 src/arch/x86_64/ps2.h create mode 100644 src/input/ps2.c create mode 100644 src/input/ps2.h diff --git a/ext2_root/init.elf b/ext2_root/init.elf index 203b9618605bee2b9fdf75249db1407fd0061e9d..b7bbf25753527a650b57038b476ccbafaca5e4f1 100755 GIT binary patch delta 352 zcmeyM{y}|$1Y`6>Nq55!3=jaL83Z7F79a(}4N!gqg9BKiVd6w}K`esu8((apKTo~IK%zpjPsM z;^+VW|2>W$umR~c{6D!;Ae=E{@*PQo zeNMPPCU**%b5=lgS4=)BWY4%`GN-V;=nbfkS3pf<1JW#$V}-Rj8w4Rj>XRo5n{zIQ J^4x(uc>qa{J5K-r diff --git a/src/arch/x86_64/apic.c b/src/arch/x86_64/apic.c new file mode 100644 index 0000000..3511e50 --- /dev/null +++ b/src/arch/x86_64/apic.c @@ -0,0 +1,147 @@ +#include "apic.h" +#include "mm/vmm.h" +#include "mm/memory.h" +#include "stdio.h" + +/* ── Internal state ───────────────────────────────────────────────────────── */ +static volatile uint32_t *g_lapic = NULL; /* Virtual address of LAPIC MMIO */ + +/* ── Low-level helpers ────────────────────────────────────────────────────── */ + +static inline uint32_t lapic_read(uint32_t reg) { + return g_lapic[reg >> 2]; +} + +static inline void lapic_write(uint32_t reg, uint32_t val) { + g_lapic[reg >> 2] = val; + /* Serialise: read back a read-only register to ensure the write landed + * before we continue (required by the APIC spec). */ + (void)g_lapic[LAPIC_ID >> 2]; +} + +static inline void rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi) { + asm volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); +} + +static inline void wrmsr(uint32_t msr, uint32_t lo, uint32_t hi) { + asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); +} + +/* ── Public API ───────────────────────────────────────────────────────────── */ + +uint64_t lapic_get_phys_base(void) { + uint32_t lo, hi; + rdmsr(IA32_APIC_BASE_MSR, &lo, &hi); + /* Physical base is in bits [35:12] of the 64-bit MSR */ + return ((uint64_t)(hi & 0xFu) << 32) | (lo & 0xFFFFF000u); +} + +void lapic_eoi(void) { + lapic_write(LAPIC_EOI, 0); +} + +uint32_t lapic_id(void) { + /* On xAPIC the LAPIC ID sits in bits [31:24] of the ID register */ + return lapic_read(LAPIC_ID) >> 24; +} + +void lapic_init(void) { + /* + * ── Step 1: Re-enable the APIC global enable bit ───────────────────── + * + * Earlier in kmain (before pmm_init) we cleared bit 11 of IA32_APIC_BASE + * so that the i8259 PIC would raise interrupts properly under Limine. + * Now that the LAPIC is being brought online we must restore that bit; + * without it the LAPIC is completely off and nothing below will work. + */ + uint32_t lo, hi; + rdmsr(IA32_APIC_BASE_MSR, &lo, &hi); + lo |= IA32_APIC_BASE_ENABLE; + wrmsr(IA32_APIC_BASE_MSR, lo, hi); + + /* + * ── Step 2: Locate the LAPIC MMIO window ───────────────────────────── + * + * The physical address is almost always 0xFEE00000 but we read it from + * the MSR to be correct. vmm_init() already identity-maps all physical + * memory through the HHDM (up to 256 GiB), so the MMIO page is reachable + * at phys + MEM_PHYS_OFFSET with no extra mapping needed. + * + * TODO: For strict correctness the LAPIC page should be mapped as + * uncacheable (PAT / PCD). In practice, QEMU / BOCHS work fine + * because MTRRs mark the 0xFEE00000 range as UC by default. + */ + uint64_t phys_base = ((uint64_t)(hi & 0xFu) << 32) | (lo & 0xFFFFF000u); + printf("[LAPIC] Physical base: 0x%08x%08x\n", + (uint32_t)(phys_base >> 32), (uint32_t)phys_base); + + g_lapic = (volatile uint32_t *)(phys_base + MEM_PHYS_OFFSET); + + /* + * ── Step 3: Software-enable the LAPIC via the SVR ──────────────────── + * + * Bit 8 = Software Enable. The lower 8 bits are the "spurious interrupt + * vector" delivered if the CPU acknowledges an interrupt that was + * retracted by the LAPIC; 0xFF is the conventional choice. + */ + lapic_write(LAPIC_SVR, LAPIC_SVR_ENABLE | 0xFF); + + /* + * ── Step 4: Configure LINT0 as ExtINT (i8259 pass-through) ────────── + * + * On a standard PC the i8259 INTR pin is wired to the BSP's LINT0. + * Setting LINT0 to ExtINT delivery mode makes the LAPIC act as a + * transparent relay: the i8259 interrupt acknowledge cycle goes through + * normally, the full 8-bit vector comes from the i8259, and the CPU's + * existing IDT entries + irq.c EOI code all keep working unchanged. + * + * This is the "Virtual Wire" mode described in the Intel MP Spec. + */ + lapic_write(LAPIC_LVT_LINT0, LAPIC_LVT_DM_EXTINT); + + /* + * ── Step 5: Configure LINT1 as NMI ─────────────────────────────────── + * + * LINT1 is the NMI pin on standard PCs. Delivery mode = 4 (NMI), + * unmasked, edge-triggered (the default when LAPIC_LVT_LEVEL is clear). + */ + lapic_write(LAPIC_LVT_LINT1, LAPIC_LVT_DM_NMI); + + /* + * ── Step 6: Mask LVT entries we are not yet using ──────────────────── + * + * Timer, error, and (if present) thermal / perf entries all start masked. + * Assign distinct vectors in the 0xF0-0xFE range so that if one fires + * spuriously the IDT handler can identify it and send EOI. + */ + lapic_write(LAPIC_LVT_TIMER, LAPIC_LVT_MASKED | 0xFD); + lapic_write(LAPIC_LVT_ERROR, LAPIC_LVT_MASKED | 0xFE); + + /* Version register: bits [23:16] = max LVT entry index */ + uint32_t ver = lapic_read(LAPIC_VER); + uint32_t max_lvt = (ver >> 16) & 0xFF; + if (max_lvt >= 4) lapic_write(LAPIC_LVT_THERMAL, LAPIC_LVT_MASKED | 0xFC); + if (max_lvt >= 5) lapic_write(LAPIC_LVT_PERF, LAPIC_LVT_MASKED | 0xFB); + + /* + * ── Step 7: Clear the Error Status Register ─────────────────────────── + * + * The APIC spec requires writing ESR twice to clear stale error bits. + */ + lapic_write(LAPIC_ESR, 0); + lapic_write(LAPIC_ESR, 0); + + /* Dismiss any stale in-service interrupt */ + lapic_write(LAPIC_EOI, 0); + + /* + * ── Step 8: Set Task Priority to 0 ─────────────────────────────────── + * + * TPR = 0 means the CPU will accept all interrupt priorities. + * Raise this later if you need to block lower-priority interrupts. + */ + lapic_write(LAPIC_TPR, 0); + + printf("[LAPIC] Online. ID=%u version=0x%02x max_lvt=%u\n", + lapic_id(), ver & 0xFF, max_lvt); +} \ No newline at end of file diff --git a/src/arch/x86_64/apic.h b/src/arch/x86_64/apic.h new file mode 100644 index 0000000..00da6cc --- /dev/null +++ b/src/arch/x86_64/apic.h @@ -0,0 +1,77 @@ +#pragma once +#include +#include + +/* + * Local APIC (xAPIC mode, MMIO access) + * Register offsets are byte offsets; we shift right by 2 for uint32_t indexing. + */ + +/* ── Register offsets ─────────────────────────────────────────────────────── */ +#define LAPIC_ID 0x020 /* LAPIC ID (bits [27:24] on P4, [31:24] on modern) */ +#define LAPIC_VER 0x030 /* Version register */ +#define LAPIC_TPR 0x080 /* Task Priority Register */ +#define LAPIC_APR 0x090 /* Arbitration Priority (RO) */ +#define LAPIC_PPR 0x0A0 /* Processor Priority (RO) */ +#define LAPIC_EOI 0x0B0 /* End-Of-Interrupt (write 0 to ack) */ +#define LAPIC_RRD 0x0C0 /* Remote Read (RO) */ +#define LAPIC_LDR 0x0D0 /* Logical Destination */ +#define LAPIC_DFR 0x0E0 /* Destination Format */ +#define LAPIC_SVR 0x0F0 /* Spurious Interrupt Vector */ +#define LAPIC_ESR 0x280 /* Error Status Register */ +#define LAPIC_ICR_LO 0x300 /* Interrupt Command (low 32 bits) */ +#define LAPIC_ICR_HI 0x310 /* Interrupt Command (high 32 bits) */ +#define LAPIC_LVT_TIMER 0x320 /* LVT: APIC timer */ +#define LAPIC_LVT_THERMAL 0x330 /* LVT: Thermal sensor */ +#define LAPIC_LVT_PERF 0x340 /* LVT: Performance monitoring */ +#define LAPIC_LVT_LINT0 0x350 /* LVT: Local interrupt pin 0 */ +#define LAPIC_LVT_LINT1 0x360 /* LVT: Local interrupt pin 1 */ +#define LAPIC_LVT_ERROR 0x370 /* LVT: Error */ +#define LAPIC_TIMER_ICR 0x380 /* Timer Initial Count */ +#define LAPIC_TIMER_CCR 0x390 /* Timer Current Count (RO) */ +#define LAPIC_TIMER_DCR 0x3E0 /* Timer Divide Configuration */ + +/* ── SVR flags ────────────────────────────────────────────────────────────── */ +#define LAPIC_SVR_ENABLE (1u << 8) /* Software-enable the LAPIC */ + +/* ── LVT delivery modes (bits [10:8]) ────────────────────────────────────── */ +#define LAPIC_LVT_DM_FIXED (0u << 8) /* Fixed delivery */ +#define LAPIC_LVT_DM_SMI (2u << 8) +#define LAPIC_LVT_DM_NMI (4u << 8) /* NMI */ +#define LAPIC_LVT_DM_EXTINT (7u << 8) /* ExtINT: pass-through from i8259 */ + +/* ── LVT misc flags ───────────────────────────────────────────────────────── */ +#define LAPIC_LVT_MASKED (1u << 16) /* Mask this LVT entry */ +#define LAPIC_LVT_LEVEL (1u << 15) /* Level-triggered (vs edge) */ +#define LAPIC_LVT_ACTIVE_LOW (1u << 13) /* Active-low polarity */ + +/* ── IA32_APIC_BASE MSR ───────────────────────────────────────────────────── */ +#define IA32_APIC_BASE_MSR 0x1B +#define IA32_APIC_BASE_ENABLE (1u << 11) /* APIC global enable bit */ +#define IA32_APIC_BASE_BSP (1u << 8) /* Bootstrap processor flag */ + +/* ── Public API ───────────────────────────────────────────────────────────── */ + +/** + * lapic_init - Map the LAPIC MMIO, re-enable the APIC global bit in the MSR + * (which was cleared earlier to let the i8259 work), then bring + * the LAPIC online with LINT0=ExtINT so the i8259/PIT path is + * completely preserved. + * + * Call AFTER uacpi_initialize() so that paging and the heap are live. + */ +void lapic_init(void); + +/** + * lapic_eoi - Signal end-of-interrupt to the LAPIC. + * Must be called from interrupt handlers that go through the LAPIC + * (i.e. IOAPIC-routed interrupts). ExtINT (i8259) interrupts only + * need the i8259 EOI, which your existing irq.c already sends. + */ +void lapic_eoi(void); + +/** lapic_id - Return the LAPIC ID of the current CPU. */ +uint32_t lapic_id(void); + +/** lapic_get_phys_base - Return the physical base address from the MSR. */ +uint64_t lapic_get_phys_base(void); \ No newline at end of file diff --git a/src/arch/x86_64/ata.c b/src/arch/x86_64/ata.c index 6827ed8..a03c43e 100644 --- a/src/arch/x86_64/ata.c +++ b/src/arch/x86_64/ata.c @@ -1,5 +1,5 @@ #include "ata.h" -#include "io.h" // your existing inb/outb/insw +#include "io.h" #include "stdio.h" #include "memory.h" #include diff --git a/src/arch/x86_64/gdt.c b/src/arch/x86_64/gdt.c index 7610028..8085d85 100644 --- a/src/arch/x86_64/gdt.c +++ b/src/arch/x86_64/gdt.c @@ -47,19 +47,40 @@ extern void tss_reload(void); void x86_64_GDT_Initialize(void) { uint64_t flags; spinlock_acquire_irqsave(&s_gdt_lock, &flags); - // Kernel code - gdt.entries[1].access = 0b10011010; - gdt.entries[1].granularity = 0b00100000; - // Kernel data - gdt.entries[2].access = 0b10010010; + // null desc + memset(&gdt.entries[0], 0, sizeof(struct gdt_desc)); + // Kernel code 0x08 + gdt.entries[1].limit = 0xFFFF; + gdt.entries[1].base_low = 0; + gdt.entries[1].base_mid = 0; + gdt.entries[1].base_hi = 0; + gdt.entries[1].access = 0b10011010; + gdt.entries[1].granularity = 0b00100000; - // User data - gdt.entries[3].access = 0b11110010; + // Kernel data 0x10 + gdt.entries[2].limit = 0xFFFF; + gdt.entries[2].base_low = 0; + gdt.entries[2].base_mid = 0; + gdt.entries[2].base_hi = 0; + gdt.entries[2].access = 0b10010010; + gdt.entries[2].granularity = 0b00100000; - // User code - gdt.entries[4].access = 0b11111010; - gdt.entries[4].granularity = 0b00100000; + // User data 0x1B + gdt.entries[3].limit = 0xFFFF; + gdt.entries[3].base_low = 0; + gdt.entries[3].base_mid = 0; + gdt.entries[3].base_hi = 0; + gdt.entries[3].access = 0b11110010; + gdt.entries[3].granularity = 0b00100000; + + // User code 0x23 + gdt.entries[4].limit = 0xFFFF; + gdt.entries[4].base_low = 0; + gdt.entries[4].base_mid = 0; + gdt.entries[4].base_hi = 0; + gdt.entries[4].access = 0b11111010; + gdt.entries[4].granularity = 0b00100000; // TSS gdt.tss.length = sizeof(struct tss); diff --git a/src/arch/x86_64/i8259.h b/src/arch/x86_64/i8259.h index ef6748b..fbeb6ad 100644 --- a/src/arch/x86_64/i8259.h +++ b/src/arch/x86_64/i8259.h @@ -6,4 +6,5 @@ const PICDriver* i8259_GetDriver(); uint16_t i8259_GetMask(); void i8259_SetMask(uint16_t newMask); -void i8259_Configure(uint8_t offsetPic1, uint8_t offsetPic2, bool autoEoi); \ No newline at end of file +void i8259_Configure(uint8_t offsetPic1, uint8_t offsetPic2, bool autoEoi); +void i8259_Disable(); \ No newline at end of file diff --git a/src/arch/x86_64/ioapic.c b/src/arch/x86_64/ioapic.c new file mode 100644 index 0000000..177bcf2 --- /dev/null +++ b/src/arch/x86_64/ioapic.c @@ -0,0 +1,359 @@ +#include "ioapic.h" +#include "apic.h" +#include "mm/vmm.h" +#include "mm/memory.h" +#include "stdio.h" +#include /* uacpi_table_find_by_signature / uacpi_table_unref */ +#include "pic.h" + + +/* ══════════════════════════════════════════════════════════════════════════ + * ACPI MADT structures + * (Defined locally so we don't depend on uACPI's internal acpi.h layout.) + * ══════════════════════════════════════════════════════════════════════════ */ + +extern const PICDriver* g_Driver; // old pic driver + +bool g_IOAPIC = false; // IOAPIC enabled + +typedef struct { + uint8_t signature[4]; /* "APIC" */ + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t oem_table_id[8]; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; + /* --- MADT-specific -------------------------------------------------- */ + uint32_t local_apic_addr; /* Default LAPIC physical address (32-bit) */ + uint32_t flags; /* bit 0 = dual 8259 PICs present */ + /* followed by variable-length Interrupt Controller Structure entries */ +} __attribute__((packed)) madt_t; + +typedef struct { + uint8_t type; + uint8_t length; +} __attribute__((packed)) madt_entry_hdr_t; + +/* Type 0: Processor Local APIC */ +typedef struct { + madt_entry_hdr_t hdr; + uint8_t uid; + uint8_t apic_id; + uint32_t flags; /* bit 0 = enabled */ +} __attribute__((packed)) madt_lapic_t; + +/* Type 1: I/O APIC */ +typedef struct { + madt_entry_hdr_t hdr; + uint8_t id; + uint8_t reserved; + uint32_t address; /* Physical MMIO base */ + uint32_t gsi_base; /* First GSI handled by this IOAPIC */ +} __attribute__((packed)) madt_ioapic_t; + +/* Type 2: Interrupt Source Override */ +typedef struct { + madt_entry_hdr_t hdr; + uint8_t bus; /* 0 = ISA */ + uint8_t source; /* ISA IRQ number */ + uint32_t gsi; /* Remapped GSI */ + uint16_t flags; /* bits [1:0] = polarity, bits [3:2] = trigger mode */ +} __attribute__((packed)) madt_iso_t; + +/* Type 4: Local APIC NMI */ +typedef struct { + madt_entry_hdr_t hdr; + uint8_t uid; /* 0xFF = all processors */ + uint16_t flags; + uint8_t lint; /* 0 or 1 */ +} __attribute__((packed)) madt_nmi_t; + +/* ══════════════════════════════════════════════════════════════════════════ + * Internal state + * ══════════════════════════════════════════════════════════════════════════ */ + +typedef struct { + volatile uint32_t *base; /* Virtual address of IOAPIC MMIO */ + uint32_t gsi_base; + uint32_t gsi_count; /* Number of redirection entries */ +} ioapic_t; + +static ioapic_t g_ioapics[IOAPIC_MAX]; +static int g_ioapic_count = 0; + +/* ISA interrupt source overrides (max 16 ISA IRQs) */ +typedef struct { + bool present; + uint32_t gsi; + bool active_low; + bool level; +} iso_t; +static iso_t g_iso[16]; /* indexed by ISA IRQ (source) */ + +/* ══════════════════════════════════════════════════════════════════════════ + * Low-level IOAPIC MMIO access + * + * The IOAPIC has two MMIO registers: + * base+0x00 IOREGSEL – index register (write which register to access) + * base+0x10 IOWIN – data window (read/write the selected register) + * ══════════════════════════════════════════════════════════════════════════ */ + +static uint32_t ioapic_read(const ioapic_t *io, uint8_t reg) { + *((volatile uint32_t *)(io->base + (0x00 >> 2))) = reg; + return *((volatile uint32_t *)(io->base + (0x10 >> 2))); +} + +static void ioapic_write(const ioapic_t *io, uint8_t reg, uint32_t val) { + *((volatile uint32_t *)(io->base + (0x00 >> 2))) = reg; + *((volatile uint32_t *)(io->base + (0x10 >> 2))) = val; +} + +/* ── Find which IOAPIC owns a given GSI ─────────────────────────────────── */ + +static ioapic_t *ioapic_for_gsi(uint32_t gsi) { + for (int i = 0; i < g_ioapic_count; i++) { + ioapic_t *io = &g_ioapics[i]; + if (gsi >= io->gsi_base && gsi < io->gsi_base + io->gsi_count) + return io; + } + return NULL; +} + +/* ══════════════════════════════════════════════════════════════════════════ + * Public API + * ══════════════════════════════════════════════════════════════════════════ */ + +uint32_t ioapic_gsi_for_isa_irq(uint8_t isa_irq) { + if (isa_irq < 16 && g_iso[isa_irq].present) + return g_iso[isa_irq].gsi; + return isa_irq; +} + +void ioapic_redirect(uint32_t gsi, uint8_t vector, uint8_t dest_lapic, + bool active_low, bool level, bool masked) { + ioapic_t *io = ioapic_for_gsi(gsi); + if (!io) { + printf("[IOAPIC] No IOAPIC for GSI %u\n", gsi); + return; + } + + uint8_t idx = (uint8_t)(gsi - io->gsi_base); + uint32_t lo = (uint32_t)vector + | IOAPIC_RTE_DM_FIXED + | (active_low ? IOAPIC_RTE_ACTIVE_LOW : 0) + | (level ? IOAPIC_RTE_LEVEL : 0) + | (masked ? IOAPIC_RTE_MASKED : 0); + uint32_t hi = (uint32_t)dest_lapic << 24; + + /* Write high word first, then low (avoids momentary spurious delivery) */ + ioapic_write(io, IOAPIC_REDTBL_HI(idx), hi); + ioapic_write(io, IOAPIC_REDTBL_LO(idx), lo); +} + +void ioapic_mask_gsi(uint32_t gsi) { + ioapic_t *io = ioapic_for_gsi(gsi); + if (!io) return; + uint8_t idx = (uint8_t)(gsi - io->gsi_base); + uint32_t lo = ioapic_read(io, IOAPIC_REDTBL_LO(idx)); + ioapic_write(io, IOAPIC_REDTBL_LO(idx), lo | IOAPIC_RTE_MASKED); +} + +void ioapic_unmask_gsi(uint32_t gsi) { + ioapic_t *io = ioapic_for_gsi(gsi); + if (!io) return; + uint8_t idx = (uint8_t)(gsi - io->gsi_base); + uint32_t lo = ioapic_read(io, IOAPIC_REDTBL_LO(idx)); + ioapic_write(io, IOAPIC_REDTBL_LO(idx), lo & ~IOAPIC_RTE_MASKED); +} + +/* ══════════════════════════════════════════════════════════════════════════ + * Initialisation + * ══════════════════════════════════════════════════════════════════════════ */ + +void ioapic_init(void) { + /* + * ── Step 1: Find the MADT via uACPI ────────────────────────────────── + * + * The MADT signature in ACPI is "APIC" (not "MADT"). + * uacpi_table_find_by_signature returns a handle whose .ptr field points + * to the raw table data in (HHDM-mapped) physical memory. + */ + struct uacpi_table madt_table; + uacpi_status st = uacpi_table_find_by_signature("APIC", &madt_table); + if (uacpi_unlikely_error(st)) { + printf("[IOAPIC] Could not find MADT: %s\n", uacpi_status_to_string(st)); + return; + } + + madt_t *madt = (madt_t *)madt_table.ptr; + printf("[IOAPIC] MADT @ virt 0x%lx len=%u lapic_addr=0x%08x\n", + (uint64_t)madt, madt->length, madt->local_apic_addr); + + /* + * ── Step 2: Walk the MADT entry list ───────────────────────────────── + * + * Entries start immediately after the fixed 44-byte MADT header and run + * until madt->length bytes from the table base. + */ + const uint8_t *entry_ptr = (const uint8_t *)madt + sizeof(madt_t); + const uint8_t *madt_end = (const uint8_t *)madt + madt->length; + + while (entry_ptr < madt_end) { + const madt_entry_hdr_t *hdr = (const madt_entry_hdr_t *)entry_ptr; + + if (hdr->length < 2) { + printf("[IOAPIC] MADT entry with length < 2, stopping parse\n"); + break; + } + + switch (hdr->type) { + + /* ── Type 0: Processor Local APIC ──────────────────────────────── */ + case 0: { + const madt_lapic_t *e = (const madt_lapic_t *)entry_ptr; + printf("[IOAPIC] MADT[0] Processor UID=%u LAPIC ID=%u flags=0x%x%s\n", + e->uid, e->apic_id, e->flags, + (e->flags & 1) ? "" : " (disabled)"); + break; + } + + /* ── Type 1: I/O APIC ───────────────────────────────────────────── */ + case 1: { + const madt_ioapic_t *e = (const madt_ioapic_t *)entry_ptr; + if (g_ioapic_count >= IOAPIC_MAX) { + printf("[IOAPIC] Too many IOAPICs, skipping ID=%u\n", e->id); + break; + } + + ioapic_t *io = &g_ioapics[g_ioapic_count]; + io->gsi_base = e->gsi_base; + + /* + * Map the IOAPIC MMIO page. Like the LAPIC, the HHDM covers the + * IOAPIC's physical address (typically 0xFEC00000) so we just add + * MEM_PHYS_OFFSET. Two MMIO registers are accessed (offsets 0 and + * 0x10) so one 4 KiB page is sufficient. + * + * TODO: Mark the page UC (cache-disable) in the PTE when your VMM + * gains support for PAT / PCD flags. + */ + uint64_t phys = (uint64_t)e->address; + uintptr_t virt = (uintptr_t)phys + MEM_PHYS_OFFSET; + /* The HHDM loop in vmm_init already covered this range; if not, + * uncomment the explicit map call below: */ + /* vmm_map_page(kernel_pagemap, virt, phys, + PAGE_READ | PAGE_WRITE | PAGE_NO_EXECUTE, Size4KiB); */ + io->base = (volatile uint32_t *)virt; + + /* Read version register to discover number of redirection entries */ + uint32_t ver = ioapic_read(io, IOAPIC_REG_VER); + io->gsi_count = ((ver >> 16) & 0xFF) + 1; /* bits [23:16] = max entry index */ + + printf("[IOAPIC] MADT[1] ID=%u phys=0x%08x GSI base=%u entries=%u\n", + e->id, e->address, io->gsi_base, io->gsi_count); + + /* + * ── Mask every redirection entry ───────────────────────────── + * + * We keep all entries masked at boot time. Legacy ISA IRQs are + * handled by the i8259 → LAPIC LINT0 ExtINT path, not via the + * IOAPIC. PCI devices can be connected later with ioapic_redirect(). + */ + for (uint32_t n = 0; n < io->gsi_count; n++) { + ioapic_write(io, IOAPIC_REDTBL_HI(n), 0); + ioapic_write(io, IOAPIC_REDTBL_LO(n), IOAPIC_RTE_MASKED | 0xFF); + } + + g_ioapic_count++; + break; + } + + /* ── Type 2: Interrupt Source Override ──────────────────────────── */ + case 2: { + const madt_iso_t *e = (const madt_iso_t *)entry_ptr; + + /* flags bits [1:0]: 00/11 = conforms to bus (ISA = active high edge) + * 01 = active high, 11 = active low + * flags bits [3:2]: 00/11 = conforms to bus (ISA = edge) + * 01 = edge, 11 = level */ + bool active_low = ((e->flags & 0x3) == 3); + bool level = ((e->flags >> 2) & 0x3) == 3; + + printf("[IOAPIC] MADT[2] ISA IRQ %u -> GSI %u %s %s\n", + e->source, e->gsi, + active_low ? "active-low" : "active-high", + level ? "level" : "edge"); + + if (e->source < 16) { + g_iso[e->source].present = true; + g_iso[e->source].gsi = e->gsi; + g_iso[e->source].active_low = active_low; + g_iso[e->source].level = level; + } + break; + } + + /* ── Type 4: Local APIC NMI ─────────────────────────────────────── */ + case 4: { + const madt_nmi_t *e = (const madt_nmi_t *)entry_ptr; + printf("[IOAPIC] MADT[4] NMI UID=0x%02x LINT%u\n", + e->uid, e->lint); + break; + } + + default: + printf("[IOAPIC] MADT entry type=%u length=%u (skipped)\n", + hdr->type, hdr->length); + break; + } + + entry_ptr += hdr->length; + } + + uacpi_table_unref(&madt_table); + + printf("[IOAPIC] Init done: %d IOAPIC(s) found\n", g_ioapic_count); +} +void irq_redirect_to_apic(uint8_t isa_irq, uint8_t vector, + uint8_t dest_lapic, bool masked) +{ + if (isa_irq >= 16) { + printf("[IRQ] irq_redirect_to_apic: ISA IRQ %u out of range\n", isa_irq); + return; + } + + uint32_t gsi = ioapic_gsi_for_isa_irq(isa_irq); + + /* Get polarity/trigger from MADT ISO or use ISA defaults */ + bool active_low = false; + bool level = false; + + if (isa_irq < 16 && g_iso[isa_irq].present) { + active_low = g_iso[isa_irq].active_low; + level = g_iso[isa_irq].level; + } else { + /* Standard ISA: active-high, edge-triggered (except some like IRQ0/8) */ + if (isa_irq == 0 || isa_irq == 8) { + level = true; /* often level on real hardware */ + } + } + + /* Mask in the 8259 so it stops firing through LINT0 */ + if (g_Driver) { + g_Driver->Mask(isa_irq); // from your irq.c / i8259 + } + + /* Programme IOAPIC redirection entry */ + ioapic_redirect(gsi, vector, dest_lapic, + active_low, level, masked); + + printf("[IRQ] Redirected ISA IRQ %u -> GSI %u vector 0x%02x LAPIC %u %s %s\n", + isa_irq, gsi, vector, dest_lapic, + active_low ? "active-low" : "active-high", + level ? "level" : "edge"); + + g_IOAPIC = true; +} \ No newline at end of file diff --git a/src/arch/x86_64/ioapic.h b/src/arch/x86_64/ioapic.h new file mode 100644 index 0000000..15e7ab7 --- /dev/null +++ b/src/arch/x86_64/ioapic.h @@ -0,0 +1,76 @@ +#pragma once +#include +#include + +/* + * I/O APIC driver + * + * The IOAPIC is found by parsing the ACPI MADT table via uACPI. + * All redirection entries are masked at init time; use ioapic_redirect() / + * ioapic_unmask_irq() to enable individual GSIs for PCI devices later. + * + * Legacy ISA IRQs (0-15) are intentionally left masked here because they are + * delivered through the i8259 → LAPIC LINT0 ExtINT path instead. + */ + +/* Maximum IOAPICs supported (1 is typical for QEMU/BOCHS) */ +#define IOAPIC_MAX 4 + +/* ── IOAPIC MMIO register indices (written to IOREGSEL) ─────────────────── */ +#define IOAPIC_REG_ID 0x00 /* APIC ID */ +#define IOAPIC_REG_VER 0x01 /* Version / max redirection entries */ +#define IOAPIC_REG_ARB 0x02 /* Arbitration ID */ +#define IOAPIC_REDTBL_LO(n) (0x10 + (n) * 2) /* Redirection entry n, low */ +#define IOAPIC_REDTBL_HI(n) (0x10 + (n) * 2 + 1) /* Redirection entry n, high */ + +/* ── Redirection table entry flags (low 32-bit word) ────────────────────── */ +#define IOAPIC_RTE_MASKED (1u << 16) /* 1 = masked (disabled) */ +#define IOAPIC_RTE_LEVEL (1u << 15) /* 1 = level-triggered, 0 = edge */ +#define IOAPIC_RTE_ACTIVE_LOW (1u << 13) /* 1 = active low, 0 = active high */ +#define IOAPIC_RTE_LOGICAL (1u << 11) /* 1 = logical destination mode */ +#define IOAPIC_RTE_DM_FIXED (0u << 8) /* Fixed delivery */ +#define IOAPIC_RTE_DM_NMI (4u << 8) /* NMI */ +/* bits [7:0] hold the IDT vector */ + +/* ── Public API ──────────────────────────────────────────────────────────── */ + +/** + * ioapic_init - Parse the ACPI MADT, locate all I/O APICs and their GSI + * bases, record any ISA interrupt source overrides, then mask + * every redirection entry. + * + * Must be called after uacpi_initialize() (needs MADT accessible). + */ +void ioapic_init(void); + +/** + * ioapic_redirect - Programme a single redirection table entry. + * + * @gsi Global System Interrupt number (from MADT or known fixed GSI). + * @vector IDT vector the CPU will see (e.g. 0x20 + irq_number). + * @dest_lapic Physical LAPIC ID of the target CPU. + * @active_low true if device uses active-low polarity. + * @level true if device uses level-triggered signalling. + * @masked true to programme the entry but leave it masked. + */ +void ioapic_redirect(uint32_t gsi, uint8_t vector, uint8_t dest_lapic, + bool active_low, bool level, bool masked); + +/** + * ioapic_mask_gsi - Mask (disable) a GSI redirection entry. + * ioapic_unmask_gsi - Unmask (enable) a GSI redirection entry. + */ +void ioapic_mask_gsi(uint32_t gsi); +void ioapic_unmask_gsi(uint32_t gsi); + +/** + * ioapic_gsi_for_isa_irq - Look up the GSI that corresponds to an ISA IRQ + * number, accounting for any MADT interrupt source + * overrides (e.g. ISA IRQ 0 → GSI 2 on BOCHS). + * + * Returns the GSI, or the IRQ number itself if no override is defined. + */ +uint32_t ioapic_gsi_for_isa_irq(uint8_t isa_irq); + +void irq_redirect_to_apic(uint8_t isa_irq, uint8_t vector, uint8_t dest_lapic, bool masked); + diff --git a/src/arch/x86_64/irq.c b/src/arch/x86_64/irq.c index d9e13c9..14ed264 100644 --- a/src/arch/x86_64/irq.c +++ b/src/arch/x86_64/irq.c @@ -5,12 +5,19 @@ #include #include "stdio.h" #include +#include "apic.h" +#include "ioapic.h" #define PIC_REMAP_OFFSET 0x20 #define MODULE "PIC" IRQHandler g_IRQHandlers[16]; -static const PICDriver* g_Driver = NULL; +static IRQHandler g_APICHandlers[256]; +const PICDriver* g_Driver = NULL; + +extern bool g_IOAPIC; + + void x86_64_IRQ_Handler(Registers *regs) { @@ -26,11 +33,25 @@ void x86_64_IRQ_Handler(Registers *regs) log_warn(MODULE, "Unhandled IRQ %d...", irq); } - // send EOI g_Driver->SendEndOfInterrupt(irq); + + } +void x86_64_APIC_IRQ_Handler(Registers* regs) +{ + uint8_t vector = regs->interrupt; + + if (g_APICHandlers[vector] != NULL) { + g_APICHandlers[vector](regs); + } else { + log_warn("APIC", "Unhandled vector 0x%02x", vector); + } + + lapic_eoi(); // ← This is the key difference from PIC! +} + void x86_64_IRQ_Initialize(void) { @@ -65,6 +86,23 @@ void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler) g_IRQHandlers[irq] = handler; } +void x86_64_APIC_IRQ_RegisterHandler(uint8_t vector, IRQHandler handler) +{ + if (vector < 32) { + printf("[APIC] Warning: vector %u is in reserved range!\n", vector); + } + g_APICHandlers[vector] = handler; + x86_64_ISR_RegisterHandler(vector, x86_64_APIC_IRQ_Handler); +} + +void x86_64_APIC_IRQ_RedirectAndRegister(uint8_t isa_irq, + uint8_t vector, + IRQHandler handler) +{ + x86_64_APIC_IRQ_RegisterHandler(vector, handler); + irq_redirect_to_apic(isa_irq, vector, lapic_id(), false); +} + void x86_64_IRQ_Unmask(int irq) { if (g_Driver) g_Driver->Unmask(irq); diff --git a/src/arch/x86_64/irq.h b/src/arch/x86_64/irq.h index 34051b5..f35e664 100644 --- a/src/arch/x86_64/irq.h +++ b/src/arch/x86_64/irq.h @@ -6,4 +6,8 @@ typedef void (*IRQHandler)(Registers* regs); void x86_64_IRQ_Initialize(); void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler); -void x86_64_IRQ_Unmask(int irq); \ No newline at end of file +void x86_64_IRQ_Unmask(int irq); +void x86_64_APIC_IRQ_RegisterHandler(uint8_t vector, IRQHandler handler); +void x86_64_APIC_IRQ_RedirectAndRegister(uint8_t isa_irq, + uint8_t vector, + IRQHandler handler); \ No newline at end of file diff --git a/src/arch/x86_64/pci.h b/src/arch/x86_64/pci.h index 2932279..9119ec1 100644 --- a/src/arch/x86_64/pci.h +++ b/src/arch/x86_64/pci.h @@ -22,7 +22,7 @@ typedef struct { #define PCI_CLASS_AUDIO 0x04 #define PCI_SUBCLASS_HDA 0x03 -// Read/write config space (re-uses your existing uACPI PCI code) +// Read/write config space uint8_t pci_read8 (pci_address_t addr, uint16_t offset); uint16_t pci_read16(pci_address_t addr, uint16_t offset); uint32_t pci_read32(pci_address_t addr, uint16_t offset); diff --git a/src/arch/x86_64/pit.c b/src/arch/x86_64/pit.c index 75dda6e..8893dd8 100644 --- a/src/arch/x86_64/pit.c +++ b/src/arch/x86_64/pit.c @@ -5,6 +5,7 @@ #include "stdio.h" #include "e9.h" #include "limine.h" +#include "apic.h" __attribute__((used, section(".limine_requests"))) volatile struct limine_date_at_boot_request boot_request = { @@ -20,7 +21,7 @@ volatile struct limine_date_at_boot_request boot_request = { volatile uint64_t g_Ticks = 0; volatile uint64_t ticks = 0; uint64_t g_Unixseconds = 0; - +extern bool g_IOAPIC; /* ========================= */ /* IRQ0 Handler (Timer Tick) */ /* ========================= */ @@ -34,7 +35,10 @@ void PIT_IRQ_Handler(Registers* regs) ticks = 0; } - + if (g_IOAPIC == true) { + lapic_eoi(); + } + // You can add scheduler / time logic here later } diff --git a/src/arch/x86_64/ps2.c b/src/arch/x86_64/ps2.c deleted file mode 100644 index 770e592..0000000 --- a/src/arch/x86_64/ps2.c +++ /dev/null @@ -1,332 +0,0 @@ -#include "ps2.h" -#include "io.h" -#include "irq.h" -#include "stdio.h" -#include "mp/spinlock.h" -#include -#include "input/input.h" - -#define PS2_DATA 0x60 -#define PS2_COMMAND 0x64 - -/* ====================== CONTROLLER HELPERS ====================== */ -static inline bool ps2_wait_write(void) { - uint32_t timeout = 1000000; - while (x86_64_inb(PS2_COMMAND) & 2) { - if (--timeout == 0) { - printf("PS/2: write timeout!\n"); - return false; - } - asm volatile("pause"); - } - return true; -} - -static inline bool ps2_wait_read(void) { - uint32_t timeout = 1000000; - while (!(x86_64_inb(PS2_COMMAND) & 1)) { - if (--timeout == 0) { - printf("PS/2: read timeout!\n"); - return false; - } - asm volatile("pause"); - } - return true; -} - -static inline uint8_t ps2_read(void) { - if (!ps2_wait_read()) return 0xFF; // error value - return x86_64_inb(PS2_DATA); -} - -static inline void ps2_write(uint8_t val) { - if (!ps2_wait_write()) return; - x86_64_outb(PS2_DATA, val); -} - -static inline void ps2_command(uint8_t cmd) { - if (!ps2_wait_write()) return; - x86_64_outb(PS2_COMMAND, cmd); -} - -/* Send command to AUX (mouse) port */ -static void ps2_write_mouse(uint8_t val) { - ps2_command(0xD4); // tell controller next byte goes to mouse - ps2_write(val); -} - -/* ====================== KEYBOARD RING BUFFER ====================== */ -#define KB_BUFFER_SIZE 256 -static uint8_t kb_buffer[KB_BUFFER_SIZE]; -static size_t kb_read_idx = 0; -static size_t kb_write_idx = 0; -static spinlock_t kb_lock = SPINLOCK_INIT; - -/* US QWERTY scancode set 2 -> ASCII (unshifted) */ -static const char kb_map[128] = { -/*00*/ 0, 0, '1', '2', '3', '4', '5', '6', -/*08*/ '7', '8', '9', '0', '-', '=', '\b', '\t', -/*10*/ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', -/*18*/ 'o', 'p', '[', ']', '\n', 0, 'a', 's', -/*20*/ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', -/*28*/ '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', -/*30*/ 'b', 'n', 'm', ',', '.', '/', 0, '*', -/*38*/ 0, ' ', 0, 0, 0, 0, 0, 0, -/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, -/*48*/ 0, 0, '-', 0, 0, 0, '+', 0, -/*50*/ 0, 0, 0, 0, 0, 0, 0, 0, -/*58*/ 0, 0, 0, 0, 0, 0, 0, 0, -/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, -/*68*/ 0, 0, 0, 0, 0, 0, 0, 0, -/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, -/*78*/ 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Shifted version */ -static const char kb_map_shift[128] = { -/*00*/ 0, 0, '!', '@', '#', '$', '%', '^', -/*08*/ '&', '*', '(', ')', '_', '+', '\b', '\t', -/*10*/ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', -/*18*/ 'O', 'P', '{', '}', '\n', 0, 'A', 'S', -/*20*/ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', -/*28*/ '"', '~', 0, '|', 'Z', 'X', 'C', 'V', -/*30*/ 'B', 'N', 'M', '<', '>', '?', 0, '*', -/*38*/ 0, ' ', 0, 0, 0, 0, 0, 0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, -}; - -static bool shift = false; -static bool caps = false; - -static void kb_enqueue(uint8_t sc) { - spinlock_acquire_or_wait(&kb_lock); - size_t next = (kb_write_idx + 1) % KB_BUFFER_SIZE; - if (next != kb_read_idx) { - kb_buffer[kb_write_idx] = sc; - kb_write_idx = next; - } - spinlock_drop(&kb_lock); -} - -bool ps2_keyboard_has_data(void) { - spinlock_acquire_or_wait(&kb_lock); - bool has = (kb_read_idx != kb_write_idx); - spinlock_drop(&kb_lock); - return has; -} - - -/* ====================== MOUSE RING BUFFER ====================== */ -#define MOUSE_BUFFER_SIZE 32 -static mouse_packet_t mouse_buffer[MOUSE_BUFFER_SIZE]; -static size_t mouse_read_idx = 0; -static size_t mouse_write_idx = 0; -static spinlock_t mouse_lock = SPINLOCK_INIT; - -static uint8_t mouse_packet[4]; -static int mouse_idx = 0; -static int mouse_packet_size = 3; // 3 or 4 for wheel - -static void mouse_enqueue(const mouse_packet_t* pkt) { - spinlock_acquire_or_wait(&mouse_lock); - size_t next = (mouse_write_idx + 1) % MOUSE_BUFFER_SIZE; - if (next != mouse_read_idx) { - mouse_buffer[mouse_write_idx] = *pkt; - mouse_write_idx = next; - } - spinlock_drop(&mouse_lock); -} - -bool ps2_mouse_get_packet(mouse_packet_t* out) { - spinlock_acquire_or_wait(&mouse_lock); - if (mouse_read_idx == mouse_write_idx) { - spinlock_drop(&mouse_lock); - return false; - } - *out = mouse_buffer[mouse_read_idx]; - mouse_read_idx = (mouse_read_idx + 1) % MOUSE_BUFFER_SIZE; - spinlock_drop(&mouse_lock); - return true; -} - -bool ps2_mouse_has_packet(void) { - spinlock_acquire_or_wait(&mouse_lock); - bool has = (mouse_read_idx != mouse_write_idx); - spinlock_drop(&mouse_lock); - return has; -} - -/* ====================== IRQ HANDLERS ====================== */ -void keyboard_irq_handler(Registers* regs) { - (void)regs; - uint8_t sc = x86_64_inb(PS2_DATA); - - // Optional: keep the old buffer if you still use the polling API somewhere - kb_enqueue(sc); - - // === NEW: process and push to the input subsystem === - bool released = (sc & 0x80) != 0; - sc &= 0x7F; - - if (sc == 0x2A || sc == 0x36) { // L/R Shift - shift = !released; - return; - } - if (sc == 0x3A && !released) { // Caps Lock - caps = !caps; - return; - } - - bool is_alpha = (sc >= 0x10 && sc <= 0x19) || - (sc >= 0x1E && sc <= 0x26) || - (sc >= 0x2C && sc <= 0x32); - - bool use_upper = shift ^ (caps && is_alpha); - char ascii = use_upper ? kb_map_shift[sc] : kb_map[sc]; - - input_push_key(sc, released ? 0 : ascii, !released); -} - -void mouse_irq_handler(Registers* regs) -{ - (void)regs; - uint8_t data = x86_64_inb(PS2_DATA); - - if (mouse_idx == 0 && !(data & 0x08)) return; - - mouse_packet[mouse_idx++] = data; - - if (mouse_idx == mouse_packet_size) { - mouse_packet_t pkt = {0}; - uint8_t status = mouse_packet[0]; - - pkt.overflow_x = !!(status & (1 << 6)); - pkt.overflow_y = !!(status & (1 << 7)); - - if (pkt.overflow_x || pkt.overflow_y) { - mouse_idx = 0; - return; - } - - pkt.buttons = status & 0x07; - pkt.x_delta = (int8_t)mouse_packet[1]; - pkt.y_delta = -(int8_t)mouse_packet[2]; - pkt.z_delta = (mouse_packet_size == 4) ? (int8_t)mouse_packet[3] : 0; - - mouse_enqueue(&pkt); - - if (pkt.x_delta || pkt.y_delta) - input_push_mouse_rel(pkt.x_delta, pkt.y_delta); - if (pkt.z_delta) - input_push_mouse_wheel(pkt.z_delta); - - static uint8_t prev_buttons = 0; - uint8_t changed = pkt.buttons ^ prev_buttons; - for (int i = 0; i < 3; i++) { - if (changed & (1 << i)) - input_push_mouse_btn(i, !!(pkt.buttons & (1 << i))); - } - prev_buttons = pkt.buttons; - - mouse_idx = 0; - } -} - -/* ====================== DEVICE INITIALIZATION ====================== */ -static bool ps2_controller_init(void) { - ps2_command(0xAD); - ps2_command(0xA7); - - while (x86_64_inb(PS2_COMMAND) & 1) - x86_64_inb(PS2_DATA); - - ps2_command(0xAA); - if (ps2_read() != 0x55) { - printf("PS/2: Controller self-test failed!\n"); - return false; - } - - ps2_command(0xAB); - if (ps2_read() != 0x00) { - printf("PS/2: Keyboard interface test failed!\n"); - return false; - } - ps2_command(0xA9); - if (ps2_read() != 0x00) { - printf("PS/2: Mouse interface test failed (no mouse?)\n"); - } - - ps2_command(0x20); - uint8_t cfg = ps2_read(); - cfg |= (1 << 0) | (1 << 1) | (1 << 6); - cfg &= ~((1 << 4) | (1 << 5)); - ps2_command(0x60); - ps2_write(cfg); - - ps2_command(0xAE); - ps2_command(0xA8); - - printf("PS/2: Controller ready (IRQs enabled, translation on)\n"); - return true; -} - -static void ps2_keyboard_init(void) { - ps2_write(0xFF); - ps2_read(); // ACK - ps2_read(); ps2_read(); // AA 00 - - ps2_write(0xF4); - if (ps2_read() != 0xFA) - printf("PS/2: Keyboard enable failed\n"); - - printf("PS/2: Keyboard initialized (scancode set 2)\n"); -} - -static void ps2_mouse_init(void) { - ps2_write_mouse(0xFF); - ps2_read(); ps2_read(); ps2_read(); // ACK, AA, 00 - - ps2_write_mouse(0xF3); ps2_write_mouse(200); - ps2_write_mouse(0xF3); ps2_write_mouse(100); - ps2_write_mouse(0xF3); ps2_write_mouse(80); - ps2_write_mouse(0xF2); - uint8_t id = ps2_read(); - if (id == 0x03) { - mouse_packet_size = 4; - printf("PS/2: IntelliMouse (wheel) detected\n"); - } else { - mouse_packet_size = 3; - printf("PS/2: Standard mouse detected\n"); - } - - ps2_write_mouse(0xF6); - ps2_write_mouse(0xF4); - - printf("PS/2: Mouse initialized (packet size %d)\n", mouse_packet_size); -} - -/* ====================== MAIN INIT ====================== */ -void ps2_init(void) { - if (!ps2_controller_init()) { - printf("PS/2: Controller initialization failed - input disabled\n"); - return; - } - - input_init(); - - ps2_keyboard_init(); - ps2_mouse_init(); - - - x86_64_IRQ_RegisterHandler(1, keyboard_irq_handler); - x86_64_IRQ_RegisterHandler(12, mouse_irq_handler); - - - x86_64_IRQ_Unmask(1); - x86_64_IRQ_Unmask(12); - - printf("PS/2 subsystem initialized successfully!\n"); -} \ No newline at end of file diff --git a/src/arch/x86_64/ps2.h b/src/arch/x86_64/ps2.h deleted file mode 100644 index 39a7a4a..0000000 --- a/src/arch/x86_64/ps2.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - - -#include -#include -#include "mp/spinlock.h" - -/* ====================== PUBLIC API ====================== */ - -/* Keyboard */ -uint8_t ps2_keyboard_get_scancode(void); // non-blocking, returns 0 if empty -bool ps2_keyboard_has_data(void); -char ps2_keyboard_get_char(void); // processed ASCII (handles shift/caps) - -/* Mouse */ -typedef struct { - int8_t x_delta; - int8_t y_delta; - int8_t z_delta; // wheel (0 if not supported) - uint8_t buttons; // bit 0 = left, bit 1 = right, bit 2 = middle - bool overflow_x; - bool overflow_y; -} mouse_packet_t; - -bool ps2_mouse_get_packet(mouse_packet_t* out_packet); // non-blocking, returns false if empty -bool ps2_mouse_has_packet(void); - -/* Subsystem */ -void ps2_init(void); diff --git a/src/arch/x86_64/usermode.c b/src/arch/x86_64/usermode.c index 2a4004b..77cc2ac 100644 --- a/src/arch/x86_64/usermode.c +++ b/src/arch/x86_64/usermode.c @@ -31,32 +31,21 @@ static void setup_user_stack() for (;;); } - // Map each page individually into the current pagemap - // Stack grows downward, so start from the bottom - uintptr_t virt = USER_STACK_TOP - USER_STACK_SIZE; + uintptr_t stack_bottom = USER_STACK_TOP - USER_STACK_SIZE; + uintptr_t virt = stack_bottom; for (int i = 0; i < USER_STACK_PAGES; i++) { - uint64_t phys = user_stack_phys_base + (i * PAGE_SIZE); + uint64_t phys = user_stack_phys_base + i * PAGE_SIZE; - bool success = vmm_map_page( - kernel_pagemap, // Use kernel_pagemap for now (later: process->process_pagemap) - virt, - phys, - PAGE_READ | PAGE_WRITE | PAGE_USER, // RW + User - Size4KiB - ); - - if (!success) { - printf("Failed to map user stack page at 0x%lx\n", virt); - // TODO: cleanup previous pages if partial failure + if (!vmm_map_page(kernel_pagemap, virt, phys, + PAGE_READ | PAGE_WRITE | PAGE_USER, Size4KiB)) { + printf("Failed to map user stack at 0x%lx\n", virt); for (;;); } - virt += PAGE_SIZE; } - - // Optional: zero the stack (good practice) - // memset((void*)(USER_STACK_TOP - USER_STACK_SIZE), 0, USER_STACK_SIZE); + // Zero the stack + memset((void*)stack_bottom, 0, USER_STACK_SIZE); } @@ -65,24 +54,26 @@ static void setup_user_stack() __attribute__((naked)) void enter_user_mode(uint64_t rip, uint64_t rsp) { - asm volatile( - "cli\n" - "mov $0x1B, %ax\n" // Use User Data Selector (0x1B) - "mov %ax, %ds\n" - "mov %ax, %es\n" - "mov %ax, %fs\n" - "mov %ax, %gs\n" + asm volatile ( + "cli\n\t" + "mov $0x1B, %%ax\n\t" // User data segment + "mov %%ax, %%ds\n\t" + "mov %%ax, %%es\n\t" + "mov %%ax, %%fs\n\t" + "mov %%ax, %%gs\n\t" - "pushq $0x1B\n" // SS (User Data) - "pushq %rsi\n" // RSP - "pushfq\n" - // Optional: manually set IF bit in pushed RFLAGS if you want interrupts enabled - "pop %rax\n" - "or $0x200, %rax\n" - "push %rax\n" - "pushq $0x23\n" // CS (User Code) - "pushq %rdi\n" // RIP - "iretq\n" + "push $0x1B\n\t" // SS (user data) + "push %1\n\t" // RSP + "pushfq\n\t" + "pop %%rax\n\t" + "or $0x200, %%rax\n\t" // Enable interrupts + "push %%rax\n\t" // RFLAGS + "push $0x23\n\t" // CS (user code) + "push %0\n\t" // RIP + "iretq\n\t" + : + : "r"(rip), "r"(rsp) + : "rax", "memory" ); } @@ -95,6 +86,11 @@ void start_userspace(void) for(;;); } + if (!entry) { + printf("ELF has no entry point\n"); + for(;;); + } + setup_user_stack(); printf("Jumping to user entry: 0x%lx with stack top 0x%lx\n", diff --git a/src/input/input.c b/src/input/input.c index 537c66e..fba95c2 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -81,12 +81,7 @@ void input_push_mouse_wheel(int8_t delta) push_event(&ev); } -/* ------------------------------------------------------------------ * - * /dev/input/event0 VFS device node - * - * Adjust VFS_RegisterDevice() to match whatever your VFS layer exposes - * for character-device registration. - * ------------------------------------------------------------------ */ + static int dev_input_read(void *buf, size_t len) { size_t count = 0; diff --git a/src/input/ps2.c b/src/input/ps2.c new file mode 100644 index 0000000..05ff4a6 --- /dev/null +++ b/src/input/ps2.c @@ -0,0 +1,193 @@ +#include "ps2.h" +#include "input/input.h" +#include "arch/x86_64/irq.h" +#include "arch/x86_64/isr.h" +#include "arch/x86_64/io.h" +#include "arch/x86_64/ioapic.h" +#include "arch/x86_64/apic.h" +#include "stdio.h" + +/* ── PS/2 I/O ports ───────────────────────────────────────────────────────── */ +#define PS2_DATA_PORT 0x60 /* Read: scancode / Write: command data */ +#define PS2_STATUS_PORT 0x64 /* Read: controller status */ +#define PS2_CMD_PORT 0x64 /* Write: controller command */ + +/* Status register bits */ +#define PS2_STATUS_OBF (1 << 0) /* Output buffer full (data ready to read) */ +#define PS2_STATUS_IBF (1 << 1) /* Input buffer full (don't write yet) */ + +/* ── IRQ number for the keyboard ─────────────────────────────────────────── */ +#define KBD_IRQ 1 +#define KBD_IDT_VECTOR (0x20 + KBD_IRQ) /* 0x21 with PIC_REMAP_OFFSET=0x20 */ + +/* ── Modifier state ───────────────────────────────────────────────────────── */ +static bool s_shift = false; +static bool s_ctrl = false; +static bool s_alt = false; +static bool s_caps_lock = false; +static bool s_extended = false; /* true after receiving 0xE0 prefix */ + +/* ══════════════════════════════════════════════════════════════════════════ + * Set-1 keycode → ASCII translation tables + * + * Index = Set-1 make code (0x00–0x58 covers the full AT-101 layout). + * 0x00 = unmapped / non-printable. + * ══════════════════════════════════════════════════════════════════════════ */ + +static const char s_normal[128] = { +/*00*/ 0, 0, '1', '2', '3', '4', '5', '6', +/*08*/ '7', '8', '9', '0', '-', '=', '\b', '\t', +/*10*/ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +/*18*/ 'o', 'p', '[', ']', '\n', 0, 'a', 's', +/*20*/ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +/*28*/ '\'','`', 0, '\\','z', 'x', 'c', 'v', +/*30*/ 'b', 'n', 'm', ',', '.', '/', 0, '*', +/*38*/ 0, ' ', 0, 0, 0, 0, 0, 0, +/*40*/ 0, 0, 0, 0, 0, 0, 0, '7', +/*48*/ '8', '9', '-', '4', '5', '6', '+', '1', +/*50*/ '2', '3', '0', '.', 0, 0, 0, 0, +/*58*/ 0 +}; + +static const char s_shifted[128] = { +/*00*/ 0, 0, '!', '@', '#', '$', '%', '^', +/*08*/ '&', '*', '(', ')', '_', '+', '\b', '\t', +/*10*/ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +/*18*/ 'O', 'P', '{', '}', '\n', 0, 'A', 'S', +/*20*/ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +/*28*/ '"', '~', 0, '|', 'Z', 'X', 'C', 'V', +/*30*/ 'B', 'N', 'M', '<', '>', '?', 0, '*', +/*38*/ 0, ' ', 0, 0, 0, 0, 0, 0, +/*40*/ 0, 0, 0, 0, 0, 0, 0, '7', +/*48*/ '8', '9', '-', '4', '5', '6', '+', '1', +/*50*/ '2', '3', '0', '.', 0, 0, 0, 0, +/*58*/ 0 +}; + +/* ══════════════════════════════════════════════════════════════════════════ + * Extended (0xE0-prefixed) make codes we care about + * ══════════════════════════════════════════════════════════════════════════ */ +typedef enum { + /* Regular Set-1 make codes (non-extended) */ + SC_LSHIFT = 0x2A, + SC_RSHIFT = 0x36, + SC_LCTRL = 0x1D, + SC_LALT = 0x38, + SC_CAPSLOCK = 0x3A, + SC_BACKSPACE= 0x0E, + SC_ENTER = 0x1C, + SC_TAB = 0x0F, + SC_ESC = 0x01, + + /* Function keys */ + SC_F1 = 0x3B, SC_F2 = 0x3C, SC_F3 = 0x3D, SC_F4 = 0x3E, + SC_F5 = 0x3F, SC_F6 = 0x40, SC_F7 = 0x41, SC_F8 = 0x42, + SC_F9 = 0x43, SC_F10 = 0x44, SC_F11 = 0x57, SC_F12 = 0x58, +} scancode_t; + +/* ══════════════════════════════════════════════════════════════════════════ + * IRQ1 handler + * ══════════════════════════════════════════════════════════════════════════ */ + +void ps2_kbd_handler(Registers *regs) +{ + (void)regs; + + /* + * Always drain the output buffer even if we don't handle the byte, + * otherwise the PS/2 controller stalls and stops sending interrupts. + */ + uint8_t status = x86_64_inb(PS2_STATUS_PORT); + if (!(status & PS2_STATUS_OBF)) { + /* Spurious interrupt with no data — just return */ + return; + } + + uint8_t byte = x86_64_inb(PS2_DATA_PORT); + + /* ── Handle 0xE0 extended-key prefix ─────────────────────────────── */ + if (byte == 0xE0) { + s_extended = true; + return; + } + + /* ── Decode make / break ─────────────────────────────────────────── */ + bool pressed = !(byte & 0x80); /* bit 7 = 0 → make, 1 → break */ + uint8_t scancode = byte & 0x7F; /* strip the break bit */ + bool extended = s_extended; + s_extended = false; + + /* ── Update modifier keys ────────────────────────────────────────── */ + if (!extended) { + switch (scancode) { + case SC_LSHIFT: case SC_RSHIFT: + s_shift = pressed; + return; /* don't push a key event for bare modifiers */ + + case SC_LCTRL: + s_ctrl = pressed; + return; + + case SC_LALT: + s_alt = pressed; + return; + + case SC_CAPSLOCK: + if (pressed) s_caps_lock = !s_caps_lock; + return; + } + } + + /* ── Translate to ASCII ──────────────────────────────────────────── */ + char ascii = 0; + + if (!extended && scancode < 128) { + bool use_upper = s_shift ^ s_caps_lock; /* XOR: caps inverts shift */ + ascii = use_upper ? s_shifted[scancode] : s_normal[scancode]; + } + /* Extended keys (arrows, home, end, etc.) leave ascii=0 */ + + /* ── Push event ──────────────────────────────────────────────────── */ + input_push_key(scancode, ascii, pressed); + + /* Optionally echo to debug port for development */ + if (pressed && ascii) { + /* e9_putc(ascii); ← uncomment if you have e9 debug output */ + } +} + +/* ══════════════════════════════════════════════════════════════════════════ + * Public helpers + * ══════════════════════════════════════════════════════════════════════════ */ + +uint8_t ps2_kbd_read_scancode(void) +{ + /* Spin until data is available (for early/polled use only) */ + while (!(x86_64_inb(PS2_STATUS_PORT) & PS2_STATUS_OBF)) + asm volatile("pause"); + return x86_64_inb(PS2_DATA_PORT); +} + +/* ══════════════════════════════════════════════════════════════════════════ + * Initialisation + * ══════════════════════════════════════════════════════════════════════════ */ + +void ps2_kbd_init(void) +{ + /* + * ── Step 1: Flush the PS/2 output buffer ───────────────────────── + * + * Any leftover bytes from before boot will cause the controller to + * withhold interrupts. Drain them now. + */ + int attempts = 16; + while (attempts-- && (x86_64_inb(PS2_STATUS_PORT) & PS2_STATUS_OBF)) + (void)x86_64_inb(PS2_DATA_PORT); + + + + x86_64_APIC_IRQ_RedirectAndRegister(1, 0x21, ps2_kbd_handler); + + printf("[PS2] Keyboard driver initialised (IRQ%d vector 0x%02x)\n", + KBD_IRQ, KBD_IDT_VECTOR); +} \ No newline at end of file diff --git a/src/input/ps2.h b/src/input/ps2.h new file mode 100644 index 0000000..763ecc3 --- /dev/null +++ b/src/input/ps2.h @@ -0,0 +1,9 @@ +#pragma once +#include +#include + + + +void ps2_kbd_init(void); + +uint8_t ps2_kbd_read_scancode(void); \ No newline at end of file diff --git a/src/main.c b/src/main.c index d0b3f14..70a596b 100644 --- a/src/main.c +++ b/src/main.c @@ -27,8 +27,10 @@ #include "arch/x86_64/pci.h" #include "sound/hda.h" #include "sound/pcm.h" -#include "arch/x86_64/ps2.h" #include "input/input.h" +#include "arch/x86_64/apic.h" +#include "arch/x86_64/ioapic.h" +#include "input/ps2.h" uintptr_t g_hhdm_offset; @@ -85,11 +87,6 @@ static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_ __attribute__((used, section(".limine_requests_end"))) static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; -// GCC and Clang reserve the right to generate calls to the following -// 4 functions even if they are not directly called. -// Implement them as the C specification mandates. -// DO NOT remove or rename these functions, or stuff will eventually break! -// They CAN be moved to a different .c file. @@ -203,7 +200,6 @@ void kmain(void) { x86_64_ISR_Initialize(); x86_64_IRQ_Initialize(); x86_64_PIT_Initialize(1000); - ps2_init(); asm volatile("sti"); calibrate_tsc(); @@ -429,6 +425,7 @@ void kmain(void) { } + /*pci_init(); pci_device_t hda; @@ -445,72 +442,20 @@ void kmain(void) { }*/ - //syscall_init(); - //start_userspace(); + + + lapic_init(); + ioapic_init(); + + + irq_redirect_to_apic(0, 0x20, lapic_id(), false); printf("tst"); - printf("\n=== Input Subsystem Test ===\n"); - printf("Opening /dev/input/event0...\n"); + input_init(); + ps2_kbd_init(); - fd_t input_fd = VFS_Open("/dev/input/event0"); - if (input_fd < 0) { - printf("Failed to open /dev/input/event0!\n"); - } else { - printf("Successfully opened /dev/input/event0 (fd=%d)\n", input_fd); - printf("Press keys or move mouse... (press ESC to exit test)\n\n"); - - input_event_t ev; - bool running = true; - - while (running) { - // Non-blocking read from the input device - int bytes = VFS_Read(input_fd, (uint8_t*)&ev, sizeof(input_event_t)); - if (bytes == sizeof(input_event_t)) { - switch (ev.type) { - case INPUT_EV_KEY: - printf("KEY: scancode=0x%02X ascii='%c' (%d) pressed=%s\n", - ev.key.scancode, - ev.key.ascii ? ev.key.ascii : '?', - ev.key.ascii, - ev.key.pressed ? "YES" : "NO"); - - // Exit test on ESC key release - if (ev.key.scancode == 0x01 && !ev.key.pressed) { - printf("ESC pressed - exiting input test.\n"); - running = false; - } - break; - - case INPUT_EV_MOUSE_REL: - if (ev.rel.dx || ev.rel.dy) { - printf("MOUSE MOVE: dx=%d dy=%d\n", ev.rel.dx, ev.rel.dy); - } - break; - - case INPUT_EV_MOUSE_BTN: - printf("MOUSE BTN: button=%d pressed=%s\n", - ev.btn.button, - ev.btn.pressed ? "YES" : "NO"); - break; - - case INPUT_EV_MOUSE_WHEEL: - printf("MOUSE WHEEL: delta=%d\n", ev.wheel.delta); - break; - - default: - printf("Unknown event type %d\n", ev.type); - break; - } - } - - // Small yield so we don't spin the CPU too hard - // (you can replace with a proper sleep later) - asm volatile("pause"); - } - - VFS_Close(input_fd); - printf("Input test finished.\n"); - } + //syscall_init(); + //start_userspace(); // We're done, just hang... hcf(); diff --git a/src/syscall/syscall.c b/src/syscall/syscall.c index 7b717db..7f4245d 100644 --- a/src/syscall/syscall.c +++ b/src/syscall/syscall.c @@ -57,7 +57,7 @@ void syscall_init(void) extern void syscall_entry(void); uint64_t kernel_cs = 0x08; - uint64_t user_cs = 0x1B; + uint64_t user_cs = 0x23; uint64_t star = ((uint64_t)kernel_cs << 32) | diff --git a/src/syscall/syscall_entry.S b/src/syscall/syscall_entry.S index 06af86f..1411abf 100644 --- a/src/syscall/syscall_entry.S +++ b/src/syscall/syscall_entry.S @@ -3,8 +3,10 @@ syscall_entry: swapgs - push %rcx - push %r11 + mov %rsp, %gs:0x10 # save user rsp for per cpu + + push %rcx # save user rip + push %r11 # save rflags # Save original args before we clobber them push %rdi # orig rdi (arg1) diff --git a/src/time/time.c b/src/time/time.c index 2b0e13a..1176d93 100644 --- a/src/time/time.c +++ b/src/time/time.c @@ -41,7 +41,6 @@ void calibrate_tsc(void) // Compute elapsed time uint64_t tsc_delta = end_tsc - start_tsc; - // 100 ticks at 100 Hz = 1 second = 1,000,000 microseconds uint64_t elapsed_us = 1000000ULL; tsc_cycles_per_us = tsc_delta / elapsed_us; diff --git a/src/video/render.c b/src/video/render.c index 60994ae..a1755a8 100644 --- a/src/video/render.c +++ b/src/video/render.c @@ -263,7 +263,7 @@ void draw_image(uint32_t *img, int x0, int y0) uint32_t color = pixels[y * w + x]; - // alpha handling (TGA is ARGB in your parser) + // alpha handling uint8_t a = color >> 24; // fully transparent → skip diff --git a/user/build/crt0.o b/user/build/crt0.o index e09c594f08d00da88c95fb6155b994e42f9a5ee5..d2d1951e5ac3b6fef361e0241a103aea2cd79e80 100644 GIT binary patch delta 206 zcmZ3$K7nn5hP41A0~|PjSq=;w4D1XZ#}9yHJdPjt08;#{2!1A*|N0-p#Na>~0R|-f zVC_I6zPKc@sDxp%1f$GkOU9JR4;Wb{FJY8nRGNH{QJzt8vLcf>W75P%@yQn$1sK&Q yC&IV^Od^w)Fo`f`Oy0?4&p88XoWW#9Ci%$<%o3a@pduWT9huEJFF<+rAX5Pe@hIH@ delta 275 zcmbQhwt#(thIRrY0~|PjSq==G3>*wEKx~iW2W&u;;eUSC*Z(Fa2Z}NX)F1Wddj!%~!*lQ{8p zDo9FUav-BTqwwTHM)}DBj4W&*F$V@9SBz0&av_sEquS(&jN**J6DNvKViK8rg;4~| zp4`AFz-TyGkV%{|VzMTay=Vte9|$CXNd^XXAk8$nmPwlv>@gruX!1rTb504UyIepv F0szt@HsSyP diff --git a/user/build/init.elf b/user/build/init.elf index 203b9618605bee2b9fdf75249db1407fd0061e9d..b7bbf25753527a650b57038b476ccbafaca5e4f1 100755 GIT binary patch delta 352 zcmeyM{y}|$1Y`6>Nq55!3=jaL83Z7F79a(}4N!gqg9BKiVd6w}K`esu8((apKTo~IK%zpjPsM z;^+VW|2>W$umR~c{6D!;Ae=E{@*PQo zeNMPPCU**%b5=lgS4=)BWY4%`GN-V;=nbfkS3pf<1JW#$V}-Rj8w4Rj>XRo5n{zIQ J^4x(uc>qa{J5K-r diff --git a/user/build/init.o b/user/build/init.o index 7f50eb98d40ac21b64c6c26a3ae1e0384d9c8250..9c6a95c0445e5ab19932d34a49d494e7f67e95ed 100644 GIT binary patch delta 149 zcmbQhy@Pv#24lrUO?OShZ#x(n7#Q|D0tt`KPyDP9t`m?8V>^S{6AR=gPRL-?o*c*| zKZ%)TvJ7JmqxR-b#^;Pcu|j5Z#@UlsGTSq5pUlW8J^2PR&tw4>3C10hHCgN#Urf#f fk{2eg1dIIBG;0~63>ASj*ukx3o^Z&4=+ delta 98 zcmdnNJ%M|I24lcPP4|g2WMrDG$k@-ww0R@rb4DO%CbKzX(d3KF_Kba#1zGGF vCrl0mk_RSF1d=NzUu3bLY|qF#S(McpB*SXYd4rJw3`!>(G73-DVden<-LV^( diff --git a/user/crt0.S b/user/crt0.S index 8e15e71..4a5326e 100644 --- a/user/crt0.S +++ b/user/crt0.S @@ -1,12 +1,12 @@ -.global _start - .section .text +.global _start _start: - call main - - mov $60, %rax # exit syscall (you define this later) - xor %rdi, %rdi + mov $1, %rax + mov $'H', %rdi syscall -hang: - jmp hang \ No newline at end of file + mov $1, %rax + mov $'i', %rdi + syscall + +1: jmp 1b \ No newline at end of file