You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

保护模式下如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 03:12:07