#include "alloc.h" #include "common.h" #include "print.h" // MMIO (Memory-Mapped I/O) Mapping for ACPI and other hardware registers // // CRITICAL: MMIO regions are NOT regular RAM! // - They're not in Limine's memory map as "usable" // - They're often above the RAM limit // - Limine does NOT map them automatically // - You must manually map them before accessing // Page table entry structure struct page_entry { u64 present : 1; u64 writable : 1; u64 user : 1; u64 write_through : 1; u64 cache_disabled : 1; // IMPORTANT for MMIO! u64 accessed : 1; u64 dirty : 1; u64 huge_page : 1; u64 global : 1; u64 unused : 3; u64 frame : 40; u64 reserved : 11; u64 no_execute : 1; }; extern u64 hhdm_offset; // Get current page tables static u64 get_cr3() { u64 cr3; asm volatile("mov %%cr3, %0" : "=r"(cr3)); return cr3 & ~0xFFF; } static u64 alloc_page_table() { void *a = alloc((1 << 12) / PAGE_SIZE); if (!a) { print("Failed to allocate page table\n"); } return a ? virt_to_phys(a) : 0; } // Get or create a page table entry at any level static struct page_entry *get_or_create_table(struct page_entry *table, u64 index) { if (!table[index].present) { u64 new_table_phys = alloc_page_table(); if (new_table_phys == 0) { print("failed to alloc\n"); return NULL; } table[index].present = 1; table[index].writable = 1; table[index].user = 0; table[index].frame = new_table_phys >> 12; } u64 next_table_phys = (u64)table[index].frame << 12; return (struct page_entry *)phys_to_virt(next_table_phys); } // Map a MMIO page with proper cache settings // physical_addr: Physical address of MMIO region // size: Size in bytes (will be rounded up to 4KB pages) // Returns: Virtual address to access the MMIO region void *map_mmio(u64 physical_addr, u64 size) { print("Mapping MMIO region:\n"); print(" Physical: 0x"); print64(physical_addr); print("\n Size: 0x"); print64(size); print(" ("); print64(size >> 20); print(" MiB)\n"); // Align physical address down to page boundary u64 phys_base = physical_addr & ~(PAGE_SIZE - 1); u64 offset_in_page = physical_addr & (PAGE_SIZE - 1); // Calculate number of pages needed u64 total_size = size + offset_in_page; u64 num_pages = (total_size + PAGE_SIZE - 1) / PAGE_SIZE; print(" Aligned physical: 0x"); print64(phys_base); print("\n Offset in page: 0x"); print64(offset_in_page); print("\n Pages needed: "); print64(num_pages); print("\n"); // For MMIO, we typically map to the HHDM region // Virtual address = Physical address + HHDM offset u64 virt_base = phys_base + hhdm_offset; print(" Target virtual: 0x"); print64(virt_base); print("\n"); // Get current page tables u64 pml4_phys = get_cr3(); struct page_entry *pml4 = (struct page_entry *)phys_to_virt(pml4_phys); // Map each page for (u64 i = 0; i < num_pages; i++) { u64 current_phys = phys_base + (i * PAGE_SIZE); u64 current_virt = virt_base + (i * PAGE_SIZE); // Extract indices u64 pml4_index = (current_virt >> 39) & 0x1FF; u64 pdpt_index = (current_virt >> 30) & 0x1FF; u64 pd_index = (current_virt >> 21) & 0x1FF; u64 pt_index = (current_virt >> 12) & 0x1FF; // Walk/create page table hierarchy struct page_entry *pdpt = get_or_create_table(pml4, pml4_index); if (!pdpt) { print("ERROR: Failed to get/create PDPT\n"); return NULL; } struct page_entry *pd = get_or_create_table(pdpt, pdpt_index); if (!pd) { print("ERROR: Failed to get/create PD\n"); return NULL; } struct page_entry *pt = get_or_create_table(pd, pd_index); if (!pt) { print("ERROR: Failed to get/create PT\n"); return NULL; } // Map the page with MMIO-appropriate settings pt[pt_index].present = 1; pt[pt_index].writable = 1; pt[pt_index].user = 0; pt[pt_index].cache_disabled = 1; // CRITICAL: Disable caching for MMIO! pt[pt_index].write_through = 0; pt[pt_index].no_execute = 1; // Don't execute from MMIO pt[pt_index].frame = current_phys >> 12; // Flush TLB for this page asm volatile("invlpg (%0)" ::"r"(current_virt) : "memory"); } print(" MMIO mapped successfully!\n"); print(" Access at virtual: 0x"); print64(virt_base + offset_in_page); print("\n\n"); // Return virtual address (with original offset) return (void *)(virt_base + offset_in_page); } // Test correct mapping int test_if_mapped(u64 virt_addr) { // Try to read - if we get page fault, it's not mapped // For now, just check the page tables manually u64 pml4_idx = (virt_addr >> 39) & 0x1FF; u64 pdpt_idx = (virt_addr >> 30) & 0x1FF; u64 pd_idx = (virt_addr >> 21) & 0x1FF; u64 pt_idx = (virt_addr >> 12) & 0x1FF; // Get CR3 u64 cr3; asm volatile("mov %%cr3, %0" : "=r"(cr3)); u64 pml4_phys = cr3 & ~0xFFF; struct page_entry { u64 present : 1; u64 writable : 1; u64 user : 1; u64 write_through : 1; u64 cache_disabled : 1; u64 accessed : 1; u64 dirty : 1; u64 huge_page : 1; u64 global : 1; u64 unused : 3; u64 frame : 40; u64 reserved : 11; u64 no_execute : 1; }; // Access PML4 struct page_entry *pml4 = (struct page_entry *)(pml4_phys + hhdm_offset); if (!pml4[pml4_idx].present) { print(" PML4["); print64(pml4_idx); print("] NOT PRESENT\n"); return 0; } // Access PDPT struct page_entry *pdpt = (struct page_entry *)(((u64)pml4[pml4_idx].frame << 12) + hhdm_offset); if (!pdpt[pdpt_idx].present) { print(" PDPT["); print64(pdpt_idx); print("] NOT PRESENT\n"); return 0; } // Access PD struct page_entry *pd = (struct page_entry *)(((u64)pdpt[pdpt_idx].frame << 12) + hhdm_offset); if (!pd[pd_idx].present) { print(" PD["); print64(pd_idx); print("] NOT PRESENT\n"); return 0; } // Access PT struct page_entry *pt = (struct page_entry *)(((u64)pd[pd_idx].frame << 12) + hhdm_offset); if (!pt[pt_idx].present) { print(" PT["); print64(pt_idx); print("] NOT PRESENT\n"); return 0; } print(" All levels present\n"); print(" Physical page: 0x"); print64((u64)pt[pt_idx].frame << 12); print("\n"); print(" Cache disabled: "); print8(pt[pt_idx].cache_disabled); print("\n"); return 1; } // Unmap MMIO region void unmap_mmio(void *virtual_addr, u64 size) { u64 virt_base = (u64)virtual_addr & ~0xFFF; u64 num_pages = (size + ((u64)virtual_addr & 0xFFF) + 0xFFF) / 4096; u64 pml4_phys = get_cr3(); struct page_entry *pml4 = (struct page_entry *)phys_to_virt(pml4_phys); for (u64 i = 0; i < num_pages; i++) { u64 current_virt = virt_base + (i * 4096); u64 pml4_index = (current_virt >> 39) & 0x1FF; u64 pdpt_index = (current_virt >> 30) & 0x1FF; u64 pd_index = (current_virt >> 21) & 0x1FF; u64 pt_index = (current_virt >> 12) & 0x1FF; if (!pml4[pml4_index].present) continue; struct page_entry *pdpt = phys_to_virt((u64)pml4[pml4_index].frame << 12); if (!pdpt[pdpt_index].present) continue; struct page_entry *pd = phys_to_virt((u64)pdpt[pdpt_index].frame << 12); if (!pd[pd_index].present) continue; struct page_entry *pt = phys_to_virt((u64)pd[pd_index].frame << 12); if (!pt[pt_index].present) continue; // Clear the entry *((u64 *)&pt[pt_index]) = 0; // Flush TLB asm volatile("invlpg (%0)" ::"r"(current_virt) : "memory"); } } // Check if a physical address is already mapped int is_mapped(u64 physical_addr) { u64 virtual_addr = physical_addr + hhdm_offset; u64 pml4_phys = get_cr3(); struct page_entry *pml4 = phys_to_virt(pml4_phys); u64 pml4_index = (virtual_addr >> 39) & 0x1FF; u64 pdpt_index = (virtual_addr >> 30) & 0x1FF; u64 pd_index = (virtual_addr >> 21) & 0x1FF; u64 pt_index = (virtual_addr >> 12) & 0x1FF; if (!pml4[pml4_index].present) return 0; struct page_entry *pdpt = phys_to_virt((u64)pml4[pml4_index].frame << 12); if (!pdpt[pdpt_index].present) return 0; // Check for 1GB huge page if (pdpt[pdpt_index].huge_page) return 1; struct page_entry *pd = phys_to_virt((u64)pdpt[pdpt_index].frame << 12); if (!pd[pd_index].present) return 0; // Check for 2MB huge page if (pd[pd_index].huge_page) return 1; struct page_entry *pt = phys_to_virt((u64)pd[pd_index].frame << 12); return pt[pt_index].present; } // Read from ACPI register (8-bit) u8 acpi_read8(void *mmio_base, u64 offset) { volatile u8 *addr = (volatile u8 *)((u64)mmio_base + offset); return *addr; } // Read from ACPI register (16-bit) u16 acpi_read16(void *mmio_base, u64 offset) { volatile u16 *addr = (volatile u16 *)((u64)mmio_base + offset); return *addr; } // Read from ACPI register (32-bit) u32 acpi_read32(void *mmio_base, u64 offset) { volatile u32 *addr = (volatile u32 *)((u64)mmio_base + offset); return *addr; } // Read from ACPI register (64-bit) u64 acpi_read64(void *mmio_base, u64 offset) { volatile u64 *addr = (volatile u64 *)((u64)mmio_base + offset); return *addr; } // Write to ACPI register (8-bit) void acpi_write8(void *mmio_base, u64 offset, u8 value) { volatile u8 *addr = (volatile u8 *)((u64)mmio_base + offset); *addr = value; } // Write to ACPI register (16-bit) void acpi_write16(void *mmio_base, u64 offset, u16 value) { volatile u16 *addr = (volatile u16 *)((u64)mmio_base + offset); *addr = value; } // Write to ACPI register (32-bit) void acpi_write32(void *mmio_base, u64 offset, u32 value) { volatile u32 *addr = (volatile u32 *)((u64)mmio_base + offset); *addr = value; } // Write to ACPI register (64-bit) void acpi_write64(void *mmio_base, u64 offset, u64 value) { volatile u64 *addr = (volatile u64 *)((u64)mmio_base + offset); *addr = value; } // Example: Map and access ACPI PM1a Control Block void *map_acpi_pm1a_control(u64 physical_addr) { print("Mapping ACPI PM1a Control Block...\n"); // PM1a Control is typically 2 bytes, but map at least one page void *mmio = map_mmio(physical_addr, 4096); if (mmio) { print("PM1a Control Block mapped at virtual: 0x"); print64((u64)mmio); print("\n"); } return mmio; } // Example: Map ACPI tables (FADT, MADT, etc.) void *map_acpi_table(u64 physical_addr, u64 size) { print("Mapping ACPI table...\n"); print(" Physical: 0x"); print64(physical_addr); print("\n Size: "); print64(size); print(" bytes\n"); void *table = map_mmio(physical_addr, size); if (table) { print("ACPI table mapped at virtual: 0x"); print64((u64)table); print("\n"); } return table; } void print_page_mapping(u64 virtual_addr) { print("Page mapping for virtual 0x"); print64(virtual_addr); print(":\n"); u64 pml4_phys = get_cr3(); struct page_entry *pml4 = phys_to_virt(pml4_phys); u64 pml4_index = (virtual_addr >> 39) & 0x1FF; u64 pdpt_index = (virtual_addr >> 30) & 0x1FF; u64 pd_index = (virtual_addr >> 21) & 0x1FF; u64 pt_index = (virtual_addr >> 12) & 0x1FF; print(" Indices: PML4["); print64(pml4_index); print("] PDPT["); print64(pdpt_index); print("] PD["); print64(pd_index); print("] PT["); print64(pt_index); print("]\n"); if (!pml4[pml4_index].present) { print(" PML4 entry not present!\n"); return; } print(" PML4 entry present, frame=0x"); print64((u64)pml4[pml4_index].frame << 12); print("\n"); struct page_entry *pdpt = phys_to_virt((u64)pml4[pml4_index].frame << 12); if (!pdpt[pdpt_index].present) { print(" PDPT entry not present!\n"); return; } print(" PDPT entry present, frame=0x"); print64((u64)pdpt[pdpt_index].frame << 12); print("\n"); if (pdpt[pdpt_index].huge_page) { print(" 1GB huge page!\n"); return; } struct page_entry *pd = phys_to_virt((u64)pdpt[pdpt_index].frame << 12); if (!pd[pd_index].present) { print(" PD entry not present!\n"); return; } print(" PD entry present, frame=0x"); print64((u64)pd[pd_index].frame << 12); print("\n"); if (pd[pd_index].huge_page) { print(" 2MB huge page!\n"); return; } struct page_entry *pt = phys_to_virt((u64)pd[pd_index].frame << 12); if (!pt[pt_index].present) { print(" PT entry not present!\n"); return; } print(" PT entry present:\n"); print(" Physical frame: 0x"); print64((u64)pt[pt_index].frame << 12); print("\n Writable: "); print64(pt[pt_index].writable); print("\n Cache disabled: "); print64(pt[pt_index].cache_disabled); print("\n No execute: "); print64(pt[pt_index].no_execute); print("\n"); }