From: acevest Date: Fri, 2 Jan 2026 07:08:09 +0000 (+0800) Subject: 简单支持ioremap,通过ACPI读到IO APIC的物理基地址 X-Git-Url: http://repos.zhaoyanbai.com/Mou_128.png?a=commitdiff_plain;h=b8527b731a3b78f28c4dd51170272dee336b7c46;p=kernel.git 简单支持ioremap,通过ACPI读到IO APIC的物理基地址 --- diff --git a/boot/acpi.c b/boot/acpi.c index 7957c0a..2fffd03 100644 --- a/boot/acpi.c +++ b/boot/acpi.c @@ -11,6 +11,7 @@ #include #include #include +#include typedef struct { char signature[8]; // "RSD PTR " @@ -46,16 +47,18 @@ typedef struct { // 后面跟着可变长度的结构 } __attribute__((packed)) madt_t; -void parse_madt(paddr_t addr) { +void parse_madt(vaddr_t addr) { if (0 == addr) { printk("MADT addr is null\n"); return; } - page_map(addr, addr, PAGE_P | PAGE_WR); - + printk("MADT vaddr %08x\n", addr); madt_t* madt = (madt_t*)addr; printk("MADT LAPIC addr %08x flags %08x\n", madt->lapic_addr, madt->flags); + printk("MADT header size %u total length %u\n", sizeof(madt_t), madt->header.length); + + system.lapic_addr = madt->lapic_addr; uint8_t* ptr = (uint8_t*)(madt + 1); // 跳过表头 uint8_t* end = (uint8_t*)madt + madt->header.length; @@ -90,6 +93,12 @@ void parse_madt(paddr_t addr) { uint32_t ioapic_addr = *(uint32_t*)(ptr + 4); uint32_t global_irq_base = *(uint32_t*)(ptr + 8); ioapic_cnt++; + if (ioapic_cnt == 1) { + system.ioapic_addr = ioapic_addr; + } else { + // 多个IO-APIC,就不支持了 + panic("multiple IO-APIC not supported\n"); + } printk("IOAPIC id %u addr %08x global_irq_base %u\n", ioapic_id, ioapic_addr, global_irq_base); } break; case 2: { // IO APIC 中断源重映射 @@ -124,9 +133,10 @@ void parse_rsdt(paddr_t addr) { return; } - page_map(addr, addr, PAGE_P | PAGE_WR); + printk("parse rsdt\n"); + rsdt_t* rsdt = (rsdt_t*)(ioremap(PAGE_ALIGN(addr), PAGE_SIZE) + (addr - PAGE_ALIGN(addr))); + assert(rsdt != NULL); - rsdt_t* rsdt = (rsdt_t*)addr; // 验证签名 if (memcmp(rsdt->header.signature, "RSDT", 4) != 0) { panic("ACPI RSDT invalid\n"); @@ -142,9 +152,10 @@ void parse_rsdt(paddr_t addr) { for (int i = 0; i < table_count; i++) { uint32_t table_phys_addr = rsdt->table_ptrs[i]; - page_map((vaddr_t)table_phys_addr, (paddr_t)table_phys_addr, PAGE_P | PAGE_WR); - - acpi_sdt_header_t* table = (acpi_sdt_header_t*)table_phys_addr; + // TODO unioremap + printk("ACPI table %u addr %08x\n", i, table_phys_addr); + acpi_sdt_header_t* table = (acpi_sdt_header_t*)(ioremap(PAGE_ALIGN(table_phys_addr), PAGE_SIZE) + + (table_phys_addr - PAGE_ALIGN(table_phys_addr))); if (table == 0) { printk("ACPI table %u:%x is null\n", i, table_phys_addr); continue; @@ -157,11 +168,16 @@ void parse_rsdt(paddr_t addr) { } printk("ACPI table %u signature %s addr %08x len %u\n", i, sig, table_phys_addr, table->length); if (memcmp(sig, "APIC", 4) == 0) { - parse_madt((paddr_t)table); + printk("found MADT table, length %u\n", table->length); + parse_madt((vaddr_t)table); } } } +void init_acpi() { + parse_rsdt((paddr_t)system.rsdt_addr); +} + void parse_acpi(void* tag) { printk("ACPI[old].RSDP "); struct multiboot_tag_old_acpi* acpi_tag = (struct multiboot_tag_old_acpi*)tag; diff --git a/include/ioremap.h b/include/ioremap.h new file mode 100644 index 0000000..a85498e --- /dev/null +++ b/include/ioremap.h @@ -0,0 +1,15 @@ +/* + * ------------------------------------------------------------------------ + * File Name: ioremap.h + * Author: Zhao Yanbai + * 2026-01-02 13:38:08 Friday CST + * Description: none + * ------------------------------------------------------------------------ + */ + +#pragma once + +#include + +void* ioremap(paddr_t paddr, size_t size); +void iounmap(void* vaddr); diff --git a/include/kdef.h b/include/kdef.h index f27aaaa..c68904b 100644 --- a/include/kdef.h +++ b/include/kdef.h @@ -18,6 +18,9 @@ // 后续内核不映射显存了,以后可以提供映射到用户空间的功能,由用户态程序操作 #define VRAM_VADDR_SIZE (16 << 20) +// VMALLOC区 128MB +#define VMALLOC_VADDR_SIZE (128 << 20) + // 最大支持的线性地址空间为1G // 这里这样实现是为了简单,与Linux内核实现有显著不同 // Linux内核还把内存大致分为了 DMA(<16MB), NORMAL(<896MB), HIGHMEM(896~4G)的内存分区(zone) @@ -27,11 +30,15 @@ // 把内核线性地址的最高部分留给显存 // 余下的部分为支持映射其它物理内存的空间 -#define MAX_SUPT_PHYMM_SIZE (MAX_SUPT_VADDR_SIZE - VRAM_VADDR_SIZE - FIX_MAP_VADDR_SIZE) +#define MAX_SUPT_PHYMM_SIZE (MAX_SUPT_VADDR_SIZE - VMALLOC_VADDR_SIZE - VRAM_VADDR_SIZE - FIX_MAP_VADDR_SIZE) + +// 算出VMALLOC区的线性地址 +#define VMALLOC_VADDR_BASE (KERNEL_VADDR_BASE + MAX_SUPT_PHYMM_SIZE) +#define VMALLOC_VADDR_END (VMALLOC_VADDR_BASE + VMALLOC_VADDR_SIZE) // 算出显存的线性地址 // 之后要将这个地址映射到显存的物理地址 -#define VRAM_VADDR_BASE (PAGE_OFFSET + MAX_SUPT_PHYMM_SIZE) +#define VRAM_VADDR_BASE (VMALLOC_VADDR_END) #define VRAM_VADDR_END (VRAM_VADDR_BASE + VRAM_VADDR_SIZE) // 算出固定映射区的线性地址 diff --git a/include/system.h b/include/system.h index 9502b9e..bcbb8e1 100644 --- a/include/system.h +++ b/include/system.h @@ -158,8 +158,12 @@ typedef struct system { dev_t root_dev; - // 按理这个信息应该按CPU存储,简化实现 + // 按理这些信息应该按CPU存储,简化实现 lapic_t* lapic; + paddr_t lapic_addr; + + // + paddr_t ioapic_addr; #define CMD_LINE_SIZE 128 const char* cmdline; diff --git a/include/vmalloc.h b/include/vmalloc.h new file mode 100644 index 0000000..348da41 --- /dev/null +++ b/include/vmalloc.h @@ -0,0 +1,29 @@ +/* + * ------------------------------------------------------------------------ + * File Name: vmalloc.h + * Author: Zhao Yanbai + * 2026-01-02 12:21:21 Friday CST + * Description: none + * ------------------------------------------------------------------------ + */ + +#pragma once + +#include +#include + +#define VM_ALLOC 0x01 +#define VM_IOREMAP 0x02 + +typedef struct vm_struct { + vaddr_t vm_vaddr; + size_t vm_size; + paddr_t vm_paddr; + uint32_t vm_flags; + struct vm_struct* vm_next; +} vm_struct_t; + +vm_struct_t* get_vm_area(size_t size, uint32_t flags); +vm_struct_t* find_vm_area(vaddr_t vaddr); + +void free_vm_area(vm_struct_t* vm); diff --git a/kernel/apic.c b/kernel/apic.c index 153b179..24bdc89 100644 --- a/kernel/apic.c +++ b/kernel/apic.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include static inline uint32_t apic_offset_to_msr(uint32_t offset) { return 0x800 + (offset >> 4); @@ -189,6 +191,46 @@ void lapic_init() { uint32_t PPR = lapic->read(LAPIC_PPR); } +void ioapic_init() { + // 先找到RCBA: Root Complex Base Address寄存器 + uint32_t cmd = PCI_CMD(0, 31, 0, 0xF0); + printk("CMD: %08x\n", cmd); + uint32_t RCBA = pci_read_config_long(cmd); + + if ((RCBA & 1) == 0) { + panic("RCBA not enabled\n"); + } + + // RCBA + // bit[0]: 使能位 + // bit[13:1]: 保留 + // bit[31:14]: RCBA物理基地址 + // 0x3FFF == (1 << 14) - 1 + uint32_t rcba_phys_base = RCBA & (~0x3FFF); + + printk("RCBA: %08x %08x\n", RCBA, rcba_phys_base); + + // 把RCBA物理基地址映射到内核空间 + vaddr_t rcba_virt_base = (vaddr_t)ioremap(rcba_phys_base, 4 * PAGE_SIZE); + // set_fixmap(FIX_RCBA_BASE, rcba_phys_base); + // uint32_t rcba_virt_base = fixid_to_vaddr(FIX_RCBA_BASE); + printk("RCBA base %08x mapped to %08x\n", rcba_phys_base, rcba_virt_base); + + // OIC + // 位于RCBA 的 0x31FE 偏移处,是一个16位寄存器 + // bit[7:0]: APIC映射区。决定了IO APIC的间接访问寄存器的地址区间。只有在禁用IO APIC的情况下才能修。 + // bit[8]: IO APIC使能标志位。 置位使能 复位禁用 + // bit[9] 协处理器错误使能标志位 + // bit[15:10]: 保留 + // + // 上面得搞成ioremap映射, 因为0x31FE这个超过一页也就是4K了。 + uint16_t* pOIC = (uint16_t*)((uint8_t*)rcba_virt_base + 0x31FE); + printk("OIC: %04x\n", *pOIC); + *pOIC = *pOIC | (1 << 8); + printk("OIC: %04x\n", *pOIC); +} + void init_apic() { + lapic_init(); ioapic_init(); } diff --git a/kernel/ioremap.c b/kernel/ioremap.c new file mode 100644 index 0000000..341f10c --- /dev/null +++ b/kernel/ioremap.c @@ -0,0 +1,53 @@ +/* + * ------------------------------------------------------------------------ + * File Name: ioremap.c + * Author: Zhao Yanbai + * 2026-01-02 13:38:43 Friday CST + * Description: none + * ------------------------------------------------------------------------ + */ + +#include +#include +#include +#include + +void ioremap_page_range(vaddr_t vaddr, paddr_t paddr, size_t size) { + assert(PAGE_DOWN(size) == size); + + for (int i = 0; i < size / PAGE_SIZE; i++) { + page_map(vaddr + i * PAGE_SIZE, paddr + i * PAGE_SIZE, PAGE_P | PAGE_WR); + } +} + +void* ioremap(paddr_t phys_addr, size_t size) { + size = PAGE_UP(size); + + vm_struct_t* vm_area = get_vm_area(size, VM_IOREMAP); + if (!vm_area) { + return NULL; + } + + // 映射物理地址到虚拟地址 + vm_area->vm_paddr = phys_addr; + vm_area->vm_flags |= VM_IOREMAP; + + printk("ioremap: 0x%08x -> 0x%08x size %u\n", phys_addr, vm_area->vm_vaddr, vm_area->vm_size); + + ioremap_page_range(vm_area->vm_vaddr, vm_area->vm_paddr, vm_area->vm_size); + + return (void*)vm_area->vm_vaddr; +} + +void iounmap(void* addr) { + // vm_struct_t* vm_area = find_vm_area((vaddr_t)addr); + // if (!vm_area) { + // return; + // } + + // if (!(vm_area->vm_flags & VM_IOREMAP)) { + // return; + // } + + // free_vm_area(vm_area); +} diff --git a/kernel/setup.c b/kernel/setup.c index 83b65da..6cd244a 100644 --- a/kernel/setup.c +++ b/kernel/setup.c @@ -74,8 +74,6 @@ void print_kernel_version() { printk(version); } -void parse_rsdt(void* addr); - void setup_kernel() { printk("sysenter esp mode: %s\n", #if FIXED_SYSENTER_ESP_MODE @@ -88,8 +86,9 @@ void setup_kernel() { init_mm(); init_buffer(); -#if 0 - parse_rsdt(system.rsdt_addr); +#if 1 + void init_acpi(); + init_acpi(); #endif #if 1 void init_apic(); diff --git a/kernel/vmalloc.c b/kernel/vmalloc.c new file mode 100644 index 0000000..605cae8 --- /dev/null +++ b/kernel/vmalloc.c @@ -0,0 +1,132 @@ +/* + * ------------------------------------------------------------------------ + * File Name: vmalloc.c + * Author: Zhao Yanbai + * 2026-01-02 12:21:28 Friday CST + * Description: none + * ------------------------------------------------------------------------ + */ + +#include +#include +#include +#include +#include + +static vm_struct_t* vm_area_list = NULL; + +vaddr_t vm_vaddr_base = VMALLOC_VADDR_BASE + (8 * 1024 * 1024); +vaddr_t vm_vaddr_end = VMALLOC_VADDR_END; + +vm_struct_t* get_vm_area(size_t size, uint32_t flags) { + // assert size是页对齐 + assert(PAGE_DOWN(size) == size); + assert(vm_vaddr_base < vm_vaddr_end); + + size += PAGE_SIZE; // 尾部添加一个空页 + + vaddr_t vm_area_gap_bgn = 0; + vaddr_t vm_area_gap_end = 0; + + uint32_t eflags; + irq_save(eflags); + + vm_struct_t* prev = NULL; + vm_struct_t* curt = vm_area_list; + + while (curt != NULL) { + assert(curt->vm_vaddr >= vm_vaddr_base); + assert(curt->vm_vaddr + curt->vm_size <= vm_vaddr_end); + + // + vm_area_gap_bgn = prev == NULL ? vm_vaddr_base : prev->vm_vaddr + prev->vm_size; + vm_area_gap_end = curt->vm_vaddr; + + assert(vm_area_gap_bgn <= vm_area_gap_end); + + // 找到了 + if (vm_area_gap_end - vm_area_gap_bgn >= size) { + break; + } + + prev = curt; + curt = curt->vm_next; + } + + // 如果找到最后了也还没找到 + if (curt == NULL) { + vm_area_gap_bgn = prev == NULL ? vm_vaddr_base : prev->vm_vaddr + prev->vm_size; + vm_area_gap_end = vm_vaddr_end; + + assert(vm_area_gap_bgn <= vm_area_gap_end); + + if (vm_area_gap_end - vm_area_gap_bgn < size) { + irq_restore(eflags); + return NULL; + } + } + + // 创建一人vm_struct + vm_struct_t* vm_area = kmalloc(sizeof(vm_struct_t), 0); + if (vm_area == NULL) { + irq_restore(eflags); + return NULL; + } + + vm_area->vm_vaddr = vm_area_gap_bgn; + vm_area->vm_size = size; + vm_area->vm_flags = flags; + + // add to list + vm_area->vm_next = curt; + if (prev == NULL) { + vm_area_list = vm_area; + } else { + prev->vm_next = vm_area; + } + + irq_restore(eflags); + + return vm_area; +} + +vm_struct_t* find_vm_area(vaddr_t vaddr) { + assert(PAGE_DOWN(vaddr) == vaddr); + assert(vaddr >= vm_vaddr_base); + assert(vaddr < vm_vaddr_end); + assert(vm_area_list != NULL); + + vm_struct_t* curt = vm_area_list; + + while (curt != NULL) { + assert(curt->vm_size != 0); + assert(curt->vm_vaddr >= vm_vaddr_base); + assert(curt->vm_vaddr + curt->vm_size <= vm_vaddr_end); + + if (vaddr == curt->vm_vaddr) { + return curt; + } + + assert(vaddr >= curt->vm_vaddr + curt->vm_size); + + curt = curt->vm_next; + } + + return NULL; +} + +void free_vm_area(vm_struct_t* vm_area) { + assert(vm_area != NULL); + assert(vm_area->vm_size != 0); + assert(vm_area->vm_vaddr >= vm_vaddr_base); + assert(vm_area->vm_vaddr + vm_area->vm_size <= vm_vaddr_end); + assert(vm_area_list != NULL); + // TODO +} + +void* vmalloc(size_t size) { + return NULL; +} + +void vfree(void* addr) { +}