启用分页后如何正确访问物理地址0x1028的帧缓冲区
问题分析与解决方案
启用分页后无法访问帧缓冲区的核心原因是:虚拟地址与物理地址不再直接一一映射。你的代码存在两个关键问题:
- 分页后虚拟地址
0x1028虽在当前映射的前4MB范围内,但即使能读取到帧缓冲区的物理地址,该物理地址对应的虚拟地址未被映射,导致后续操作失效; - 帧缓冲区通常是连续的多页内存,若其物理地址超出前4MB范围,当前分页设置完全未覆盖该区域。
以下是具体修复步骤:
步骤1:提前保存帧缓冲区物理地址
在启用分页前读取并保存帧缓冲区的物理地址,避免分页后依赖未映射的虚拟地址读取:
// 全局/静态变量保存帧缓冲区物理地址 static dword_t framebuffer_phys = 0; void main() { // 分页初始化前读取物理地址0x1028中的帧缓冲区地址 if (!framebuffer_phys) { framebuffer_phys = *(dword_t *)0x1028; } // 初始化分页 init_paging(); // 后续处理帧缓冲区映射... }
步骤2:映射帧缓冲区物理页到虚拟地址
使用你已实现的map_page函数,将帧缓冲区的所有物理页映射到虚拟地址空间的可用区域(例如内核常用的0xC0000000起始地址):
void main() { // (接上一步代码) // 计算帧缓冲区需要映射的页数(以1920*1080*4字节为例) size_t fb_size = 1920 * 1080 * sizeof(dword_t); size_t page_count = (fb_size + PAGE_SIZES - 1) / PAGE_SIZES; // 向上取整 // 选择虚拟地址起始点 void *virtual_base = (void *)0xC0000000; void *phys_base = (void *)framebuffer_phys; // 逐页映射,MMIO设备建议禁用缓存(添加0x10|0x20标志) for (size_t i = 0; i < page_count; i++) { map_page(phys_base, virtual_base, 0x3 | 0x10 | 0x20); phys_base += PAGE_SIZES; virtual_base += PAGE_SIZES; } // 使用映射后的虚拟地址操作帧缓冲区 dword_t *framebuffer = (dword_t *)0xC0000000; framebuffer[10 * 1920 + 10] = 0xFFFFFF; // 绘制(10,10)白色像素 }
额外检查点
- MMIO缓存设置:帧缓冲区属于内存映射IO设备,禁用缓存(
PCD=0x10、PWT=0x20)可避免缓存一致性问题,确保像素绘制立即生效; - 页表权限:你的页表项使用
0x3(Supervisor、Read/Write、Present),内核态运行时权限足够; - 地址对齐:页目录和页表的
__attribute__((aligned(4096)))属性正确,符合x86分页要求。
内容的提问来源于stack exchange,提问作者Magic Magician
相关产品推荐
相关产品推荐

