Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ ninja runmin
- [x] LAPIC
- [x] IOAPIC
- [x] RTC
- [x] TSC (usable delay(), finally!)
- Generic
- [x] PCI
- [x] ISA
Expand Down
4 changes: 0 additions & 4 deletions arch/x86_64/cpu/Cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ static inline uint64_t __attribute__((always_inline)) rdtsc(void) {
return ((uint64_t)hi << 32) | lo;
}

static inline void __attribute__((always_inline)) delay(uint64_t cycles) {
while (cycles--) __asm__ volatile ("nop");
}

void DumpRegisters(RegistersDumpT* dump);
void PrintRegisters(const RegistersDumpT* dump);

Expand Down
208 changes: 208 additions & 0 deletions drivers/ACPI.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#include "ACPI.h"
#include "Console.h"
#include "Io.h"
#include "MemOps.h"
#include "StringOps.h"
#include "VMem.h"
#include "TSC.h"

static ACPIFADT* g_fadt = NULL;
static bool g_acpi_initialized = false;

// Find RSDP in BIOS memory areas
static ACPIRSDPv1* FindRSDP(void) {
// Search in EBDA (Extended BIOS Data Area)
uint16_t ebda = 0;
if (VMemGetPhysAddr(0x40E) != 0) {
ebda = *(uint16_t*)0x40E;
}
if (ebda) {
uint8_t* ebda_ptr = (uint8_t*)(ebda << 4);
for (uint32_t i = 0; i < 1024; i += 16) {
if (FastMemcmp(ebda_ptr + i, ACPI_RSDP_SIG, 8) == 0) {
return (ACPIRSDPv1*)(ebda_ptr + i);
}
}
}

// Search in BIOS ROM area (0xE0000-0xFFFFF)
for (uint32_t addr = 0xE0000; addr < 0x100000; addr += 16) {
if (FastMemcmp((void*)addr, ACPI_RSDP_SIG, 8) == 0) {
return (ACPIRSDPv1*)addr;
}
}

return NULL;
}

// Validate ACPI table checksum
static bool ValidateChecksum(void* table, uint32_t length) {
uint8_t sum = 0;
uint8_t* bytes = (uint8_t*)table;
for (uint32_t i = 0; i < length; i++) {
sum += bytes[i];
}
return sum == 0;
}

// Map ACPI table to virtual memory
static void* MapACPITable(uint32_t phys_addr, uint32_t size) {
// Align to page boundaries
uint32_t aligned_addr = phys_addr & ~0xFFF;
uint32_t offset = phys_addr - aligned_addr;
uint32_t aligned_size = ((size + offset + 0xFFF) & ~0xFFF);

void* virt_addr = VMemAlloc(aligned_size);
if (!virt_addr) return NULL;

if (VMemUnmap((uint64_t)virt_addr, aligned_size) != VMEM_SUCCESS) {
VMemFree(virt_addr, aligned_size);
return NULL;
}

if (VMemMapMMIO((uint64_t)virt_addr, aligned_addr, aligned_size, PAGE_WRITABLE | PAGE_NOCACHE) != VMEM_SUCCESS) {
VMemFree(virt_addr, aligned_size);
return NULL;
}

return (uint8_t*)virt_addr + offset;
}

bool ACPIInit(void) {
PrintKernel("ACPI: Initializing ACPI subsystem...\n");

// Find RSDP
ACPIRSDPv1* rsdp = FindRSDP();
if (!rsdp) {
PrintKernelError("ACPI: RSDP not found\n");
return false;
}

PrintKernel("ACPI: Found RSDP at 0x");
PrintKernelHex((uint64_t)rsdp);
PrintKernel("\n");

// Validate RSDP checksum
if (!ValidateChecksum(rsdp, sizeof(ACPIRSDPv1))) {
PrintKernelError("ACPI: Invalid RSDP checksum\n");
return false;
}

ACPIRSDT* rsdt = (ACPIRSDT*)MapACPITable(rsdp->rsdt_address,
sizeof(ACPISDTHeader));
if (!rsdt) {
PrintKernelError("ACPI: Failed to map RSDT\n");
return false;
}
// Validate RSDT signature
if (FastMemcmp(rsdt->header.signature, ACPI_RSDT_SIG, 4) != 0) {
PrintKernelError("ACPI: Invalid RSDT signature\n");
// Clean up the initial header‐only mapping
VMemUnmap((uint64_t)rsdt - (rsdp->rsdt_address & 0xFFF),
((sizeof(ACPISDTHeader) + (rsdp->rsdt_address & 0xFFF) + 0xFFF)
& ~0xFFF));
return false;
}
// Remap with the full table length
uint32_t rsdt_size = rsdt->header.length;
// Remember the old header mapping so we can free it afterward
void* old_rsdt = rsdt;
uint32_t old_offset = rsdp->rsdt_address & 0xFFF;
rsdt = (ACPIRSDT*)MapACPITable(rsdp->rsdt_address, rsdt_size);
// Now unmap the temporary header‐only region
VMemUnmap((uint64_t)old_rsdt - old_offset,
((sizeof(ACPISDTHeader) + old_offset + 0xFFF) & ~0xFFF));

PrintKernel("ACPI: RSDT mapped, length=");
PrintKernelInt(rsdt_size);
PrintKernel("\n");

// Find FADT
uint32_t entries = (rsdt_size - sizeof(ACPISDTHeader)) / 4;
for (uint32_t i = 0; i < entries; i++) {
ACPISDTHeader* header = (ACPISDTHeader*)MapACPITable(
rsdt->table_pointers[i],
sizeof(ACPISDTHeader)
);
if (!header) continue;

bool is_fadt = FastMemcmp(header->signature, ACPI_FADT_SIG, 4) == 0;
uint32_t table_length = header->length;
uint32_t header_offset = rsdt->table_pointers[i] & 0xFFF;

// Unmap the header mapping now that we've inspected it
VMemUnmap(
(uint64_t)header - header_offset,
( (sizeof(ACPISDTHeader) + header_offset + 0xFFF) & ~0xFFF )
);

if (is_fadt) {
PrintKernel("ACPI: Found FADT\n");
g_fadt = (ACPIFADT*)MapACPITable(
rsdt->table_pointers[i],
table_length
);
break;
}
}

PrintKernelError("ACPI: FADT not found or invalid\n");
return false;
}

void ACPIShutdown(void) {
if (!g_acpi_initialized || !g_fadt) {
PrintKernel("ACPI: Shutdown not available, using fallback\n");
outw(0x604, 0x2000);
return;
}

PrintKernel("ACPI: Initiating shutdown...\n");

// Enable ACPI mode if needed
if (g_fadt->smi_command_port && g_fadt->acpi_enable) {
PrintKernel("ACPI: Enabling ACPI mode via SMI\n");
outb(g_fadt->smi_command_port, g_fadt->acpi_enable);
}

// Try multiple shutdown methods
uint16_t shutdown_values[] = {0x2000, 0x3C00, 0x1400, 0x0000};

for (int i = 0; i < 4; i++) {
PrintKernel("ACPI: Trying shutdown value 0x");
PrintKernelHex(shutdown_values[i]);
PrintKernel(" on port 0x");
PrintKernelHex(g_fadt->pm1a_control_block);
PrintKernel("\n");

outw(g_fadt->pm1a_control_block, shutdown_values[i]);

// Wait a bit
delay(10);
}

// Fallback methods
PrintKernel("ACPI: Trying QEMU shutdown\n");
outw(0x604, 0x2000);

PrintKernel("ACPI: Trying Bochs shutdown\n");
outw(0xB004, 0x2000);

PrintKernelError("ACPI: All shutdown methods failed\n");
}

void ACPIReboot(void) {
PrintKernel("ACPI: Initiating reboot...\n");

// Try keyboard controller reset
outb(0x64, 0xFE);

PrintKernel("ACPI: falling back to triple faulting...\n");

struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) invalid_idt = { 0, 0 };
__asm__ volatile("lidt %0; int $0x03" :: "m"(invalid_idt));
__builtin_unreachable();
}
85 changes: 85 additions & 0 deletions drivers/ACPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#pragma once
#include "stdint.h"
#include "stdbool.h"

// ACPI Table signatures
#define ACPI_RSDP_SIG "RSD PTR "
#define ACPI_RSDT_SIG "RSDT"
#define ACPI_FADT_SIG "FACP"

// ACPI structures
typedef struct {
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
} __attribute__((packed)) ACPIRSDPv1;

typedef struct {
char signature[4];
uint32_t length;
uint8_t revision;
uint8_t checksum;
char oem_id[6];
char oem_table_id[8];
uint32_t oem_revision;
uint32_t creator_id;
uint32_t creator_revision;
} __attribute__((packed)) ACPISDTHeader;

typedef struct {
ACPISDTHeader header;
uint32_t table_pointers[];
} __attribute__((packed)) ACPIRSDT;

typedef struct {
ACPISDTHeader header;
uint32_t firmware_ctrl;
uint32_t dsdt;
uint8_t reserved;
uint8_t preferred_pm_profile;
uint16_t sci_interrupt;
uint32_t smi_command_port;
uint8_t acpi_enable;
uint8_t acpi_disable;
uint8_t s4bios_req;
uint8_t pstate_control;
uint32_t pm1a_event_block;
uint32_t pm1b_event_block;
uint32_t pm1a_control_block;
uint32_t pm1b_control_block;
uint32_t pm2_control_block;
uint32_t pm_timer_block;
uint32_t gpe0_block;
uint32_t gpe1_block;
uint8_t pm1_event_length;
uint8_t pm1_control_length;
uint8_t pm2_control_length;
uint8_t pm_timer_length;
uint8_t gpe0_length;
uint8_t gpe1_length;
uint8_t gpe1_base;
uint8_t cstate_control;
uint16_t worst_c2_latency;
uint16_t worst_c3_latency;
uint16_t flush_size;
uint16_t flush_stride;
uint8_t duty_offset;
uint8_t duty_width;
uint8_t day_alarm;
uint8_t month_alarm;
uint8_t century;
uint16_t boot_architecture_flags;
uint8_t reserved2;
uint32_t flags;
} __attribute__((packed)) ACPIFADT;

// Power management defines
#define ACPI_SLP_TYP_MASK 0x1C00
#define ACPI_SLP_EN 0x2000

// Function declarations
bool ACPIInit(void);
void ACPIShutdown(void);
void ACPIReboot(void);
Loading
Loading