首页 > 其他分享 >0178-开启四级分页

0178-开启四级分页

时间:2024-07-14 09:30:13浏览次数:14  
标签:0178 分页 mov eax 地址 四级 table page

环境

  • Time 2022-11-12
  • WSL-Ubuntu 22.04
  • QEMU 6.2.0
  • NASM 2.15.05

前言

说明

参考:https://os.phil-opp.com/entering-longmode

目标

如果要进入长模式,则必须要进行分页。在 64 位系统中,采用 4 级分页。
关于分页的信息,需要找其它的资料另外了解,这里不做详细介绍。

介绍

一共四级分页,原文中将其称为 P4,P3,P2,P1,这里也直接使用这种方式。
最初出发点为 CR3 寄存器,然后到 P4,直到 P1,最后是页偏移。
一个页表包含 512 项,每一项 8 个字节,一共 4K。
地址中的 48-63 必须满足和第 47 位一致,才是合法的地址。
具体的对应如下图:

四级分页

低地址位的状态

因为至少是 4K 分页,所以低 12 位默认都是 0,可以用它们来表示一些额外的信息。

Bit(s) Name Meaning
0 present the page is currently in memory
1 writable it’s allowed to write to this page
2 user accessible if not set, only kernel mode code can access this page
3 write through caching writes go directly to memory
4 disable cache no cache is used for this page
5 accessed the CPU sets this bit when this page is used
6 dirty the CPU sets this bit when a write to this page occurs
7 huge page/null must be 0 in P1 and P4, creates a 1GiB page in P3, creates a 2MiB page in P2
8 global page isn’t flushed from caches on address space switch (PGE bit of CR4 register must be set)
9-11 available can be used freely by the OS
52-62 available can be used freely by the OS
63 no execute forbid executing code on this page (the NXE bit in the EFER register must be set)

巨型页

除了可以使用 4K 分页,也可以使用 1G 或者 2M 分页。
为了保证从保护模式的虚拟地址到分页的正确映射,下面使用了 2M 分页。

section .bss
align 4096
p4_table:
    resb 4096
p3_table:
    resb 4096
p2_table:
    resb 4096

分页首先需要 4K 对其,然后建立了 P4,P3,P2 三个页表。

关联多级分页

; 将 P4 的第一个地址设置成 P3 的起始地址
mov eax, p3_table
or eax, 0b11 ; 二进制数,表示当前页存在,并且可写
mov [p4_table], eax

; 将 P3 的第一个地址设置成 P2 的起始地址
mov eax, p2_table
or eax, 0b11 ; 二进制数,表示当前页存在,并且可写
mov [p3_table], eax

初始化巨型页

    mov ecx, 0         ; 循环的计数器
.map_p2_table:
    ; 使用 EAX 初始化 P2 的每一项,并且映射到物理地址最低的 1G 空间
    mov eax, 0x200000  ; 2MiB
    mul ecx            ; 每一项对应的物理地址 EAX * counter
    or eax, 0b10000011 ; 存在,可写,巨型页
    mov [p2_table + ecx * 8], eax ; 将地址记录到 P2 的每一项

    inc ecx            ; 计数器加 1
    cmp ecx, 512       ; 是否存满,最大 512 项
    jne .map_p2_table  ; 不相等继续下次循环

    ret

开启分页

如果要开启分页,需要完成以下几步:

  1. 将 P4 的起始地址存放到 CR3,因为分页从 CR3 开始找。
  2. 启用物理地址扩展(PAE)。
  3. 扩展特性启用寄存器(Extended Feature Enable Register)需要设置长模式。
  4. 启用分页,由 CR0 的最高位控制。
enable_paging:
    ; 将 CR3 寄存器指向 P4 的起始地址
    mov eax, p4_table
    mov cr3, eax

    ; 在 CR4 中启用物理地址扩展(Physical Address Extension),第五位
    mov eax, cr4
    or eax, 1 << 5
    mov cr4, eax

    ; 将 EFER MSR(model specific register)寄存器中的第八位设置成长模式
    mov ecx, 0xC0000080
    rdmsr
    or eax, 1 << 8
    wrmsr

    ; 将 CR0 的最高位分页开启位设置成 1
    mov eax, cr0
    or eax, 1 << 31
    mov cr0, eax

    ret

总结

在保护模式下,开启了四级分页。

附录

源码

section .multiboot_header
header_start:
    dd 0x1BADB002  ; 魔法数字,固定值
    dd 0
    dd -0x1BADB002 ; 定义的这三个数字相加需要等于0
header_end:

global start
section .text
bits 32
start:

    ; 栈是否高地址往低地址增长
    mov esp, stack_top

    call check_cpuid
    call check_long_mode

    call set_up_page_tables
    call enable_paging

    ; print `OK` to screen
    mov dword [0xb8000], 0x2f4b2f4f
    hlt

check_cpuid:
    ; 检查 CPUID 是否支持可以通过翻转 ID 位,即第 21 位。
    ; 如果在 FLAGS 标志寄存器中,我们能够翻转它,CPUID 就是可用的。

    ; 通过栈拷贝 FLAGS 寄存器的值到 EAX 寄存器
    pushfd
    pop eax

    ; 将 EAX 的值拷贝到 ECX,后面要用
    mov ecx, eax

    ; 翻转第 21 位
    xor eax, 1 << 21

    ; 把 EAX 的值拷贝回 FLAGS 寄存器
    push eax
    popfd

    ; 拷贝 FLAGS 寄存器的值回 EAX 寄存器,检查是否翻转成功,成功翻转则支持 CPUID
    pushfd
    pop eax

    ; 通过 ECX 还原 EFLAGS 中的值
    push ecx
    popfd

    ; 比较,如果两个一样,则翻转不成功,不支持CPUID;如果翻转成功,则支持CPUID
    cmp eax, ecx
    je .no_cpuid
    ret
.no_cpuid:
    mov al, "1"
    jmp error

check_long_mode:

    ; 检查是否有扩展的处理器信息可用
    mov eax, 0x80000000    ; CPUID 的隐式参数
    cpuid                  ; 获取最高支持的参数
    cmp eax, 0x80000001    ; 如果支持长模式,至少是 0x80000001
    jb .no_long_mode       ; 如果小于,则不支持长模式

    ; 使用扩展信息验证是否支持长模式
    mov eax, 0x80000001    ; 扩展处理器参数信息
    cpuid                  ; 将各种特征标记位返回到 ECX 和 EDX
    test edx, 1 << 29      ; 第 29 位是 long mode 长模式标记位,检查是否支持
    jz .no_long_mode       ; 如果为 0,表示不支持长模式
    ret
.no_long_mode:
    mov al, "2"
    jmp error

set_up_page_tables:

    ; 将 P4 的第一个地址设置成 P3 的起始地址
    mov eax, p3_table
    or eax, 0b11 ; 二进制数,表示当前页存在,并且可写
    mov [p4_table], eax

    ; 将 P3 的第一个地址设置成 P2 的起始地址
    mov eax, p2_table
    or eax, 0b11 ; 二进制数,表示当前页存在,并且可写
    mov [p3_table], eax

    ; 将 P2 设置成 2M 的巨型页
    mov ecx, 0         ; 循环的计数器
.map_p2_table:
    ; 使用 EAX 初始化 P2 的每一项,并且映射到物理地址最低的 1G 空间
    mov eax, 0x200000  ; 2MiB
    mul ecx            ; 每一项对应的物理地址 EAX * counter
    or eax, 0b10000011 ; 存在,可写,巨型页
    mov [p2_table + ecx * 8], eax ; 将地址记录到 P2 的每一项

    inc ecx            ; 计数器加 1
    cmp ecx, 512       ; 是否存满,最大 512 项
    jne .map_p2_table  ; 不相等继续下次循环

    ret

enable_paging:
    ; 将 CR3 寄存器指向 P4 的起始地址
    mov eax, p4_table
    mov cr3, eax

    ; 在 CR4 中启用物理地址扩展(Physical Address Extension),第五位
    mov eax, cr4
    or eax, 1 << 5
    mov cr4, eax

    ; 将 EFER MSR(model specific register)寄存器中的第八位设置成长模式
    mov ecx, 0xC0000080
    rdmsr
    or eax, 1 << 8
    wrmsr

    ; 将 CR0 的最高位分页开启位设置成 1
    mov eax, cr0
    or eax, 1 << 31
    mov cr0, eax

    ret

; 打印 `ERR: ` 和一个错误代码并停住。
; 错误代码在 al 寄存器中
error:
    mov dword [0xb8000], 0x4f524f45
    mov dword [0xb8004], 0x4f3a4f52
    mov dword [0xb8008], 0x4f204f20
    mov byte  [0xb800a], al
    hlt

section .bss
align 4096
p4_table:
    resb 4096
p3_table:
    resb 4096
p2_table:
    resb 4096
stack_bottom:
    resb 64
stack_top:

标签:0178,分页,mov,eax,地址,四级,table,page
From: https://www.cnblogs.com/jiangbo4444/p/18301110

相关文章

  • 分页查询及其拓展应用案例
    分页查询分页查询是处理大量数据时常用的技术,通过分页可以将数据分成多个小部分,方便用户逐页查看。SQLAlchemy提供了简单易用的方法来实现分页查询。本篇我们也会在最终实现这样的分页效果:1.什么是分页查询分页查询是将查询结果按照一定数量分成多页展示,每页显示固定数量的......
  • 自定义PageHelper分页工具
    今天写了一个需要用到分页查询的需求大概是这样的有一张项目表和一张报警表如下:项目表:报警表:现在我就是要根据这个项目名称,报警类型和报警时间来查询报警列表.项目可以模糊查询,看似是很简单的一个需求,但是我遇到了一个问题我的大概思路就是先用项目名称去项目表......
  • 流式查询1. mybatis的游标Cursor,分页大数据查询
    流式查询流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用可以通过迭代器每次取一条查询结果。流式查询的好处是能够降低内存使用。例如我们想要从数据库取1000万条记录而又没有足够的内存时,就不得不分页查询。而分页查询就需要我们按照顺序查询并设置一个参......
  • vue简单分页
    constapp={data(){return{info:[],//存储题目详情数组currentPage:1,//当前页码pageSize:3,//每页显示的题目数量sjid:sjid};},......
  • 基于bootstrap与jQuery实现的分页功能(多余部分省略号代替)
    基于bootstrap与jQuery实现的分页功能(多余部分省略号代替) <navaria-label='Pagenavigation'id='nav_navigation'></nav><scripttype='text/javascript'>/***刷新页码方法*@paramtotalPage*@paramcurrentPage*/functionrefres......
  • 24、Django-网站分页功能-Pagintor类
    -Django提供了Paginator类可以方便的实现分页功能-Painator类位于django.core.pagintor模块中负责分页数据整体的管理对象的构造方法:paginator=Paginator(object_list,per_page)-参数:-object_list:需要分页的数据的对象列表-数据的个数-......
  • MybatisPlus使用分页功能
    MybatisPlus使用分页功能分页查询是一个很常见的需求,故Mybatis-Plus提供了一个分页插件,使用它可以十分方便的完成分页查询。下面介绍Mybatis-Plus分页插件的用法,详细信息可参考[官方文档](分页插件|MyBatis-Plus(baomidou.com))首先为分页编写一个配置类:@Configurationpub......
  • MP实现分页条件查询
     publicclassDishController{@AutowiredprivateDishServicedishService;​@GetMapping("/page")publicResult<PageResult>page(DishPageQueryDTOdto){PageResultpage=dishService.page(dto);returnResu......
  • 58.分页器
    分页器【1】模板classPagination(object):def__init__(self,current_page,all_count,per_page_num=2,pager_count=11):"""封装分页相关数据:paramcurrent_page:当前页:paramall_count:数据库中的数据总条数:par......
  • GESP 202406 四级认证 T1 题解
    大意:一个只包含000和111的矩形,边长为......