0178-开启四级分页

news/2024/9/19 15:42:31/文章来源:https://www.cnblogs.com/jiangbo4444/p/18301110

环境

  • 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  ; 2MiBmul ecx            ; 每一项对应的物理地址 EAX * counteror eax, 0b10000011 ; 存在,可写,巨型页mov [p2_table + ecx * 8], eax ; 将地址记录到 P2 的每一项inc ecx            ; 计数器加 1cmp 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_tablemov cr3, eax; 在 CR4 中启用物理地址扩展(Physical Address Extension),第五位mov eax, cr4or eax, 1 << 5mov cr4, eax; 将 EFER MSR(model specific register)寄存器中的第八位设置成长模式mov ecx, 0xC0000080rdmsror eax, 1 << 8wrmsr; 将 CR0 的最高位分页开启位设置成 1mov eax, cr0or eax, 1 << 31mov cr0, eaxret

总结

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

附录

源码

section .multiboot_header
header_start:dd 0x1BADB002  ; 魔法数字,固定值dd 0dd -0x1BADB002 ; 定义的这三个数字相加需要等于0
header_end:global start
section .text
bits 32
start:; 栈是否高地址往低地址增长mov esp, stack_topcall check_cpuidcall check_long_modecall set_up_page_tablescall enable_paging; print `OK` to screenmov dword [0xb8000], 0x2f4b2f4fhltcheck_cpuid:; 检查 CPUID 是否支持可以通过翻转 ID 位,即第 21 位。; 如果在 FLAGS 标志寄存器中,我们能够翻转它,CPUID 就是可用的。; 通过栈拷贝 FLAGS 寄存器的值到 EAX 寄存器pushfdpop eax; 将 EAX 的值拷贝到 ECX,后面要用mov ecx, eax; 翻转第 21 位xor eax, 1 << 21; 把 EAX 的值拷贝回 FLAGS 寄存器push eaxpopfd; 拷贝 FLAGS 寄存器的值回 EAX 寄存器,检查是否翻转成功,成功翻转则支持 CPUIDpushfdpop eax; 通过 ECX 还原 EFLAGS 中的值push ecxpopfd; 比较,如果两个一样,则翻转不成功,不支持CPUID;如果翻转成功,则支持CPUIDcmp eax, ecxje .no_cpuidret
.no_cpuid:mov al, "1"jmp errorcheck_long_mode:; 检查是否有扩展的处理器信息可用mov eax, 0x80000000    ; CPUID 的隐式参数cpuid                  ; 获取最高支持的参数cmp eax, 0x80000001    ; 如果支持长模式,至少是 0x80000001jb .no_long_mode       ; 如果小于,则不支持长模式; 使用扩展信息验证是否支持长模式mov eax, 0x80000001    ; 扩展处理器参数信息cpuid                  ; 将各种特征标记位返回到 ECX 和 EDXtest edx, 1 << 29      ; 第 29 位是 long mode 长模式标记位,检查是否支持jz .no_long_mode       ; 如果为 0,表示不支持长模式ret
.no_long_mode:mov al, "2"jmp errorset_up_page_tables:; 将 P4 的第一个地址设置成 P3 的起始地址mov eax, p3_tableor eax, 0b11 ; 二进制数,表示当前页存在,并且可写mov [p4_table], eax; 将 P3 的第一个地址设置成 P2 的起始地址mov eax, p2_tableor eax, 0b11 ; 二进制数,表示当前页存在,并且可写mov [p3_table], eax; 将 P2 设置成 2M 的巨型页mov ecx, 0         ; 循环的计数器
.map_p2_table:; 使用 EAX 初始化 P2 的每一项,并且映射到物理地址最低的 1G 空间mov eax, 0x200000  ; 2MiBmul ecx            ; 每一项对应的物理地址 EAX * counteror eax, 0b10000011 ; 存在,可写,巨型页mov [p2_table + ecx * 8], eax ; 将地址记录到 P2 的每一项inc ecx            ; 计数器加 1cmp ecx, 512       ; 是否存满,最大 512 项jne .map_p2_table  ; 不相等继续下次循环retenable_paging:; 将 CR3 寄存器指向 P4 的起始地址mov eax, p4_tablemov cr3, eax; 在 CR4 中启用物理地址扩展(Physical Address Extension),第五位mov eax, cr4or eax, 1 << 5mov cr4, eax; 将 EFER MSR(model specific register)寄存器中的第八位设置成长模式mov ecx, 0xC0000080rdmsror eax, 1 << 8wrmsr; 将 CR0 的最高位分页开启位设置成 1mov eax, cr0or eax, 1 << 31mov cr0, eaxret; 打印 `ERR: ` 和一个错误代码并停住。
; 错误代码在 al 寄存器中
error:mov dword [0xb8000], 0x4f524f45mov dword [0xb8004], 0x4f3a4f52mov dword [0xb8008], 0x4f204f20mov byte  [0xb800a], alhltsection .bss
align 4096
p4_table:resb 4096
p3_table:resb 4096
p2_table:resb 4096
stack_bottom:resb 64
stack_top:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/743410.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

『比赛记录』【LGR-193】洛谷 7 月月赛 IABC 362

『比赛记录』【LGR-193】洛谷 7 月月赛 IABC 362最舒服的一集「CROI R2」在相思树下 I 想了好久还是决定把这道题也写一下,毕竟赛事花了 \(40min\) 才解决。 思路 开比赛,看题面,很快啊,打了一个双端队列的做法,结果 MLE,然后人傻了二十分钟。 之后缓过神来开始推式子。…

[DP] 数位DP

本文主要内容:数位DP例题数位DP 主要有两种方法,填数法和记搜。这里主要研究记搜的实现; 模板 相比于填数法,记搜的优点在于有固定的模板,实现较容易; 缺点在于需要很多 $ memset $,常数较大,容易被卡; 下面通过几道例题来了解记搜模板; 一 $ haha $ 数设记搜各参数如…

数据库的触发器的使用

需要实现在passenger中添加乘客信息后,在classes(班次)中对应舱位的已售出票数+1例:在passenger中添加了一个degree=“头等舱”的乘客,就在classes中对应头等舱已售出票数goodnumber+1 (经济舱类似,对应secondnumber+1实现方法: 在passenger表中增加触发器,在新增pa…

【比赛】CSP提高组模拟1

挂的分比我打的都多和初三学长们一起打的比赛,被人家爆杀了。 T1 最短路 20Pts 原题 Cow Toll Paths G。 正解是按点权排序后跑一遍 Floyd,歪解是多迭代几遍。 赛时没开 long long \(80 \to 20\)。点击查看代码 #include<bits/stdc++.h> #define int ll using namespa…

トヨタ自動車プログラミングコンテスト2024#7(ABC 362)

非常好名次,使我的 \(1\) 旋转 四发罚时应该是这次比赛最唐的东西了,没有就进前一千了 A.Buy a Pen 特判秒了,懒得打三种 ans=,所以就把不能选的那个赋值成无穷大了 #include<bits/stdc++.h> using namespace std; #define speed ios::sync_with_stdio(false); #defi…

领域驱动设计:CQRS 和事件源的强大功能

CQRS/ES 如何重新定义构建可扩展系统。 在过去的 15 年里,我一直在深入研究领域驱动设计 (DDD),每一步都在学习和改进。我一直很好奇,尤其是当你深入挖掘时,简单的客户请求会变得更加复杂。我特别感谢EventStorming这样的工具,它帮助揭示看似简单需求背后的真实流程和事件…

k8s简介

一、K8S 概览 1)K8S 是什么? K8S 是Kubernetes的全称,源于希腊语,意为“舵手”或“飞行员”,官方称其是:用于自动部署、扩展和管理“容器化(containerized)应用程序”的开源系统。翻译成大白话就是:“K8S 是负责自动化运维管理多个跨机器 Docker 程序的集群”。 2) K…

flutter Error: unable to locate asset entry in pubspec.yaml: assets/fonts/Lato-Regular.ttf

在pubspec.yaml中添加font的时候出现这个问题 发现是因为我放的文件夹不对,需要放在根目录下(但是我不知道为什么android studio里没有显示一些文件夹) 本来放在这里 一直不对后来在文件夹找了一下放到lib里 新建了assets文件夹

详解工单系统

工单系统在人类早期的行为活动中就一直存在的理念,是人类关于工作流程的管理和记录。随着计算机软件的不断发展,工单系统由线下转为线上,提升了问题传达的准确性、处理效率以及数据的长久留痕可追溯等,应用领域也日渐广泛。本文将着重介绍工单的概念、不同领域工单系统简介…

Vue 3 后端错误消息处理范例

前端如何存储处理后端返回的错误信息,并按不同来源绑定到页面,例如显示在不同输入框的周围。这样即可实现清晰的错误显示。1. 错误消息格式 前后端消息传递时,我们可以通过 json 的 errors 字段传递错误信息,一个比较好的格式范例为: {errors: {global: ["网络错误&q…

OpenDiary 24.7

致敬传奇耐拖王现在是,7 月 13 日。距离上一次写日记过去了 58 天 致敬传奇耐拖王 xiwon那么,在鼠鼠肘赢牢大之前的这段时间,what happened, on earth?五月下旬,前去了西安邀请赛,打的一坨。回来之后写了一点游记,但是居然还没有写完 那么到底为什么没有写完呢?这要从很…