保护模式下如何将0xb8000地址映射到自身?(Linux最小内核场景)
无ioremap依赖的VGA显存直接访问汇编实现
针对你需要绕过ioremap、直接映射0xb8000物理地址到虚拟地址的需求,以下是x86架构下的汇编实现,直接操作页表完成映射,无需依赖页分配器:
32位保护模式实现
.global write_to_vga_direct write_to_vga_direct: # 可选:保存寄存器(不在意退出的话可省略) push %eax push %ebx push %ecx push %edx # 获取当前页目录基址(CR3寄存器存储页目录物理地址) mov %cr3, %eax # 计算0xb8000对应的页目录项索引:0xb8000 >> 22 = 0x2E mov $0x2E, %ebx # 页目录项地址 = CR3基址 + 索引*4(32位页表项占4字节) lea (%eax, %ebx, 4), %eax # 读取页目录项内容,提取页表物理基址 mov (%eax), %edx and $0xFFFFF000, %edx # 清除低12位属性位,保留页表基址 # 计算0xb8000对应的页表项索引:(0xb8000 >> 12) & 0x3FF = 0xB8 mov $0xB8, %ebx # 页表项地址 = 页表基址 + 索引*4 lea (%edx, %ebx, 4), %eax # 设置页表项:物理地址0xb8000 + 属性位0x7(P=1, RW=1, US=0) mov $0xB8007, %edx mov %edx, (%eax) # 刷新TLB,确保新映射立即生效 invlpg 0xb8000 # 写入VGA显存:黑底白字输出"Hello World" mov $0xb8000, %eax movb $'H', (%eax) movb $0x07, 1(%eax) movb $'e', 2(%eax) movb $0x07, 3(%eax) movb $'l', 4(%eax) movb $0x07, 5(%eax) movb $'l', 6(%eax) movb $0x07, 7(%eax) movb $'o', 8(%eax) movb $0x07, 9(%eax) movb $' ', 10(%eax) movb $0x07, 11(%eax) movb $'W', 12(%eax) movb $0x07, 13(%eax) movb $'o', 14(%eax) movb $0x07, 15(%eax) movb $'r', 16(%eax) movb $0x07, 17(%eax) movb $'l', 18(%eax) movb $0x07, 19(%eax) movb $'d', 20(%eax) movb $0x07, 21(%eax) # 可选:恢复寄存器 pop %edx pop %ecx pop %ebx pop %eax # 死循环,无需退出 jmp .
64位模式实现
.global write_to_vga_direct_x64 write_to_vga_direct_x64: # 可选:保存寄存器 push %rax push %rbx push %rcx push %rdx # 获取CR3基址(64位页表项占8字节) mov %cr3, %rax # 页全局目录项索引:0xb8000 >> 39 = 0 mov $0, %rbx lea (%rax, %rbx, 8), %rax mov (%rax), %rdx and $0xFFFFFFFFFFFFF000, %rdx # 清除低12位属性 # 页上级目录项索引:(0xb8000 >> 30) & 0x1FF = 0 mov $0, %rbx lea (%rdx, %rbx, 8), %rax mov (%rax), %rdx and $0xFFFFFFFFFFFFF000, %rdx # 页中间目录项索引:(0xb8000 >> 21) & 0x1FF = 0 mov $0, %rbx lea (%rdx, %rbx, 8), %rax mov (%rax), %rdx and $0xFFFFFFFFFFFFF000, %rdx # 页表项索引:(0xb8000 >> 12) & 0x1FF = 0xB8 mov $0xB8, %rbx lea (%rdx, %rbx, 8), %rax # 设置页表项:物理地址0xb8000 + 属性位0x7 mov $0xB8007, %rdx mov %rdx, (%rax) # 刷新TLB invlpg 0xb8000 # 写入VGA显存(同32位逻辑) mov $0xb8000, %rax movb $'H', (%rax) movb $0x07, 1(%rax) # 后续字符写入逻辑省略,与32位部分一致 # 可选:恢复寄存器 pop %rdx pop %rcx pop %rbx pop %rax # 死循环 jmp .
关键说明
- 直接修改内核页表会破坏原有映射,仅适用于测试场景,不可在正常运行的内核中使用。
- 页表项属性位
0x7表示:Present(映射有效)、Read/Write(可读写)、Supervisor(仅内核态访问)。 invlpg指令用于刷新TLB缓存,确保新的页表映射立即生效,避免CPU使用旧的缓存映射。- VGA显存的每个字符占2字节:低字节是ASCII码,高字节是显示属性(
0x07为黑底白字,可按需修改)。
内容的提问来源于stack exchange,提问作者d33tah
相关产品推荐
相关产品推荐

