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 <vpshinomiya@protonmail.com>
This commit is contained in:
Binary file not shown.
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "ata.h"
|
#include "ata.h"
|
||||||
#include "io.h" // your existing inb/outb/insw
|
#include "io.h"
|
||||||
#include "stdio.h"
|
#include "stdio.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|||||||
+25
-4
@@ -47,17 +47,38 @@ extern void tss_reload(void);
|
|||||||
void x86_64_GDT_Initialize(void) {
|
void x86_64_GDT_Initialize(void) {
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
spinlock_acquire_irqsave(&s_gdt_lock, &flags);
|
spinlock_acquire_irqsave(&s_gdt_lock, &flags);
|
||||||
// Kernel code
|
|
||||||
|
// 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].access = 0b10011010;
|
||||||
gdt.entries[1].granularity = 0b00100000;
|
gdt.entries[1].granularity = 0b00100000;
|
||||||
|
|
||||||
// Kernel data
|
// 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].access = 0b10010010;
|
||||||
|
gdt.entries[2].granularity = 0b00100000;
|
||||||
|
|
||||||
// User data
|
// 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].access = 0b11110010;
|
||||||
|
gdt.entries[3].granularity = 0b00100000;
|
||||||
|
|
||||||
// User code
|
// 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].access = 0b11111010;
|
||||||
gdt.entries[4].granularity = 0b00100000;
|
gdt.entries[4].granularity = 0b00100000;
|
||||||
|
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ uint16_t i8259_GetMask();
|
|||||||
void i8259_SetMask(uint16_t newMask);
|
void i8259_SetMask(uint16_t newMask);
|
||||||
|
|
||||||
void i8259_Configure(uint8_t offsetPic1, uint8_t offsetPic2, bool autoEoi);
|
void i8259_Configure(uint8_t offsetPic1, uint8_t offsetPic2, bool autoEoi);
|
||||||
|
void i8259_Disable();
|
||||||
@@ -0,0 +1,359 @@
|
|||||||
|
#include "ioapic.h"
|
||||||
|
#include "apic.h"
|
||||||
|
#include "mm/vmm.h"
|
||||||
|
#include "mm/memory.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
#include <uacpi/tables.h> /* 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
+40
-2
@@ -5,12 +5,19 @@
|
|||||||
#include <util/arrays.h>
|
#include <util/arrays.h>
|
||||||
#include "stdio.h"
|
#include "stdio.h"
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
#include "apic.h"
|
||||||
|
#include "ioapic.h"
|
||||||
|
|
||||||
#define PIC_REMAP_OFFSET 0x20
|
#define PIC_REMAP_OFFSET 0x20
|
||||||
#define MODULE "PIC"
|
#define MODULE "PIC"
|
||||||
|
|
||||||
IRQHandler g_IRQHandlers[16];
|
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)
|
void x86_64_IRQ_Handler(Registers *regs)
|
||||||
{
|
{
|
||||||
@@ -26,9 +33,23 @@ void x86_64_IRQ_Handler(Registers *regs)
|
|||||||
log_warn(MODULE, "Unhandled IRQ %d...", irq);
|
log_warn(MODULE, "Unhandled IRQ %d...", irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
// send EOI
|
|
||||||
g_Driver->SendEndOfInterrupt(irq);
|
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!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -65,6 +86,23 @@ void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler)
|
|||||||
g_IRQHandlers[irq] = 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)
|
void x86_64_IRQ_Unmask(int irq)
|
||||||
{
|
{
|
||||||
if (g_Driver) g_Driver->Unmask(irq);
|
if (g_Driver) g_Driver->Unmask(irq);
|
||||||
|
|||||||
@@ -7,3 +7,7 @@ typedef void (*IRQHandler)(Registers* regs);
|
|||||||
void x86_64_IRQ_Initialize();
|
void x86_64_IRQ_Initialize();
|
||||||
void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler);
|
void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler);
|
||||||
void x86_64_IRQ_Unmask(int irq);
|
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);
|
||||||
@@ -22,7 +22,7 @@ typedef struct {
|
|||||||
#define PCI_CLASS_AUDIO 0x04
|
#define PCI_CLASS_AUDIO 0x04
|
||||||
#define PCI_SUBCLASS_HDA 0x03
|
#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);
|
uint8_t pci_read8 (pci_address_t addr, uint16_t offset);
|
||||||
uint16_t pci_read16(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);
|
uint32_t pci_read32(pci_address_t addr, uint16_t offset);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "stdio.h"
|
#include "stdio.h"
|
||||||
#include "e9.h"
|
#include "e9.h"
|
||||||
#include "limine.h"
|
#include "limine.h"
|
||||||
|
#include "apic.h"
|
||||||
|
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
volatile struct limine_date_at_boot_request boot_request = {
|
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 g_Ticks = 0;
|
||||||
volatile uint64_t ticks = 0;
|
volatile uint64_t ticks = 0;
|
||||||
uint64_t g_Unixseconds = 0;
|
uint64_t g_Unixseconds = 0;
|
||||||
|
extern bool g_IOAPIC;
|
||||||
/* ========================= */
|
/* ========================= */
|
||||||
/* IRQ0 Handler (Timer Tick) */
|
/* IRQ0 Handler (Timer Tick) */
|
||||||
/* ========================= */
|
/* ========================= */
|
||||||
@@ -34,6 +35,9 @@ void PIT_IRQ_Handler(Registers* regs)
|
|||||||
ticks = 0;
|
ticks = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_IOAPIC == true) {
|
||||||
|
lapic_eoi();
|
||||||
|
}
|
||||||
|
|
||||||
// You can add scheduler / time logic here later
|
// You can add scheduler / time logic here later
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,332 +0,0 @@
|
|||||||
#include "ps2.h"
|
|
||||||
#include "io.h"
|
|
||||||
#include "irq.h"
|
|
||||||
#include "stdio.h"
|
|
||||||
#include "mp/spinlock.h"
|
|
||||||
#include <stddef.h>
|
|
||||||
#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");
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#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);
|
|
||||||
+31
-35
@@ -31,32 +31,21 @@ static void setup_user_stack()
|
|||||||
for (;;);
|
for (;;);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map each page individually into the current pagemap
|
uintptr_t stack_bottom = USER_STACK_TOP - USER_STACK_SIZE;
|
||||||
// Stack grows downward, so start from the bottom
|
uintptr_t virt = stack_bottom;
|
||||||
uintptr_t virt = USER_STACK_TOP - USER_STACK_SIZE;
|
|
||||||
|
|
||||||
for (int i = 0; i < USER_STACK_PAGES; i++) {
|
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(
|
if (!vmm_map_page(kernel_pagemap, virt, phys,
|
||||||
kernel_pagemap, // Use kernel_pagemap for now (later: process->process_pagemap)
|
PAGE_READ | PAGE_WRITE | PAGE_USER, Size4KiB)) {
|
||||||
virt,
|
printf("Failed to map user stack at 0x%lx\n", 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
|
|
||||||
for (;;);
|
for (;;);
|
||||||
}
|
}
|
||||||
|
|
||||||
virt += PAGE_SIZE;
|
virt += PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
// Zero the stack
|
||||||
// Optional: zero the stack (good practice)
|
memset((void*)stack_bottom, 0, USER_STACK_SIZE);
|
||||||
// memset((void*)(USER_STACK_TOP - USER_STACK_SIZE), 0, USER_STACK_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -66,23 +55,25 @@ __attribute__((naked))
|
|||||||
void enter_user_mode(uint64_t rip, uint64_t rsp)
|
void enter_user_mode(uint64_t rip, uint64_t rsp)
|
||||||
{
|
{
|
||||||
asm volatile (
|
asm volatile (
|
||||||
"cli\n"
|
"cli\n\t"
|
||||||
"mov $0x1B, %ax\n" // Use User Data Selector (0x1B)
|
"mov $0x1B, %%ax\n\t" // User data segment
|
||||||
"mov %ax, %ds\n"
|
"mov %%ax, %%ds\n\t"
|
||||||
"mov %ax, %es\n"
|
"mov %%ax, %%es\n\t"
|
||||||
"mov %ax, %fs\n"
|
"mov %%ax, %%fs\n\t"
|
||||||
"mov %ax, %gs\n"
|
"mov %%ax, %%gs\n\t"
|
||||||
|
|
||||||
"pushq $0x1B\n" // SS (User Data)
|
"push $0x1B\n\t" // SS (user data)
|
||||||
"pushq %rsi\n" // RSP
|
"push %1\n\t" // RSP
|
||||||
"pushfq\n"
|
"pushfq\n\t"
|
||||||
// Optional: manually set IF bit in pushed RFLAGS if you want interrupts enabled
|
"pop %%rax\n\t"
|
||||||
"pop %rax\n"
|
"or $0x200, %%rax\n\t" // Enable interrupts
|
||||||
"or $0x200, %rax\n"
|
"push %%rax\n\t" // RFLAGS
|
||||||
"push %rax\n"
|
"push $0x23\n\t" // CS (user code)
|
||||||
"pushq $0x23\n" // CS (User Code)
|
"push %0\n\t" // RIP
|
||||||
"pushq %rdi\n" // RIP
|
"iretq\n\t"
|
||||||
"iretq\n"
|
:
|
||||||
|
: "r"(rip), "r"(rsp)
|
||||||
|
: "rax", "memory"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +86,11 @@ void start_userspace(void)
|
|||||||
for(;;);
|
for(;;);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
printf("ELF has no entry point\n");
|
||||||
|
for(;;);
|
||||||
|
}
|
||||||
|
|
||||||
setup_user_stack();
|
setup_user_stack();
|
||||||
|
|
||||||
printf("Jumping to user entry: 0x%lx with stack top 0x%lx\n",
|
printf("Jumping to user entry: 0x%lx with stack top 0x%lx\n",
|
||||||
|
|||||||
+1
-6
@@ -81,12 +81,7 @@ void input_push_mouse_wheel(int8_t delta)
|
|||||||
push_event(&ev);
|
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)
|
static int dev_input_read(void *buf, size_t len)
|
||||||
{
|
{
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|||||||
+193
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ps2_kbd_init(void);
|
||||||
|
|
||||||
|
uint8_t ps2_kbd_read_scancode(void);
|
||||||
+15
-70
@@ -27,8 +27,10 @@
|
|||||||
#include "arch/x86_64/pci.h"
|
#include "arch/x86_64/pci.h"
|
||||||
#include "sound/hda.h"
|
#include "sound/hda.h"
|
||||||
#include "sound/pcm.h"
|
#include "sound/pcm.h"
|
||||||
#include "arch/x86_64/ps2.h"
|
|
||||||
#include "input/input.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;
|
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")))
|
__attribute__((used, section(".limine_requests_end")))
|
||||||
static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER;
|
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_ISR_Initialize();
|
||||||
x86_64_IRQ_Initialize();
|
x86_64_IRQ_Initialize();
|
||||||
x86_64_PIT_Initialize(1000);
|
x86_64_PIT_Initialize(1000);
|
||||||
ps2_init();
|
|
||||||
asm volatile("sti");
|
asm volatile("sti");
|
||||||
calibrate_tsc();
|
calibrate_tsc();
|
||||||
|
|
||||||
@@ -429,6 +425,7 @@ void kmain(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*pci_init();
|
/*pci_init();
|
||||||
|
|
||||||
pci_device_t hda;
|
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("tst");
|
||||||
|
|
||||||
printf("\n=== Input Subsystem Test ===\n");
|
input_init();
|
||||||
printf("Opening /dev/input/event0...\n");
|
ps2_kbd_init();
|
||||||
|
|
||||||
fd_t input_fd = VFS_Open("/dev/input/event0");
|
//syscall_init();
|
||||||
if (input_fd < 0) {
|
//start_userspace();
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're done, just hang...
|
// We're done, just hang...
|
||||||
hcf();
|
hcf();
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ void syscall_init(void)
|
|||||||
extern void syscall_entry(void);
|
extern void syscall_entry(void);
|
||||||
|
|
||||||
uint64_t kernel_cs = 0x08;
|
uint64_t kernel_cs = 0x08;
|
||||||
uint64_t user_cs = 0x1B;
|
uint64_t user_cs = 0x23;
|
||||||
|
|
||||||
uint64_t star =
|
uint64_t star =
|
||||||
((uint64_t)kernel_cs << 32) |
|
((uint64_t)kernel_cs << 32) |
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
syscall_entry:
|
syscall_entry:
|
||||||
swapgs
|
swapgs
|
||||||
|
|
||||||
push %rcx
|
mov %rsp, %gs:0x10 # save user rsp for per cpu
|
||||||
push %r11
|
|
||||||
|
push %rcx # save user rip
|
||||||
|
push %r11 # save rflags
|
||||||
|
|
||||||
# Save original args before we clobber them
|
# Save original args before we clobber them
|
||||||
push %rdi # orig rdi (arg1)
|
push %rdi # orig rdi (arg1)
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ void calibrate_tsc(void)
|
|||||||
// Compute elapsed time
|
// Compute elapsed time
|
||||||
uint64_t tsc_delta = end_tsc - start_tsc;
|
uint64_t tsc_delta = end_tsc - start_tsc;
|
||||||
|
|
||||||
// 100 ticks at 100 Hz = 1 second = 1,000,000 microseconds
|
|
||||||
uint64_t elapsed_us = 1000000ULL;
|
uint64_t elapsed_us = 1000000ULL;
|
||||||
|
|
||||||
tsc_cycles_per_us = tsc_delta / elapsed_us;
|
tsc_cycles_per_us = tsc_delta / elapsed_us;
|
||||||
|
|||||||
+1
-1
@@ -263,7 +263,7 @@ void draw_image(uint32_t *img, int x0, int y0)
|
|||||||
|
|
||||||
uint32_t color = pixels[y * w + x];
|
uint32_t color = pixels[y * w + x];
|
||||||
|
|
||||||
// alpha handling (TGA is ARGB in your parser)
|
// alpha handling
|
||||||
uint8_t a = color >> 24;
|
uint8_t a = color >> 24;
|
||||||
|
|
||||||
// fully transparent → skip
|
// fully transparent → skip
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+8
-8
@@ -1,12 +1,12 @@
|
|||||||
.global _start
|
|
||||||
|
|
||||||
.section .text
|
.section .text
|
||||||
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
call main
|
mov $1, %rax
|
||||||
|
mov $'H', %rdi
|
||||||
mov $60, %rax # exit syscall (you define this later)
|
|
||||||
xor %rdi, %rdi
|
|
||||||
syscall
|
syscall
|
||||||
|
|
||||||
hang:
|
mov $1, %rax
|
||||||
jmp hang
|
mov $'i', %rdi
|
||||||
|
syscall
|
||||||
|
|
||||||
|
1: jmp 1b
|
||||||
Reference in New Issue
Block a user