0180-进入 64 位模式

news/2024/9/19 15:46:00/文章来源:https://www.cnblogs.com/jiangbo4444/p/18301114

环境

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

前言

说明

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

目标

从保护模式切换到长模式。

定位代码段

因为当前还是执行的 32 的指令,所以需要执行跳转,重新选择 GDT,这里给代码段加了一个标记。

gdt64:dq 0 ; 和之前一样,第一段为 0
.code: equ $ - gdt64 ; 需要跳转到代码段; 43 表示代码段,44 同样为 1,47 表示可用,53 表示 64 位dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; 代码段

跳转指令

和之前一样,如果模式切换了,需要执行远跳指令,刷新流水线,重新加载指令。

...
extern long_mode_start
...; 远跳指令,清空流水线,执行 64 位指令
jmp gdt64.code:long_mode_start

同时增加了一个 extern 64 位的入口。

64 位汇编

global long_mode_startsection .text
bits 64
long_mode_start:; 清空所有的段寄存器,因为当前为平坦模式,不需要段选择器mov ax, 0mov ss, axmov ds, axmov es, axmov fs, axmov gs, ax; print `OKAY` to screenmov rax, 0x2f592f412f4b2f4fmov qword [0xb8000], raxhlt

修改编译和链接

#! /usr/bin/bashnasm -f elf32 -g boot.asm
nasm -f elf32 -g long_mode.asm
ld -T linker.ld -m elf_i386 boot.o long_mode.o -o kernel.elf
qemu-system-x86_64 -kernel kernel.elf -display curses -s -S

效果

切换到长模式

总结

从之前的保护模式,经过一系列的操作,进入了长模式,即 64 位模式。

附录

long_mode.asm

global long_mode_startsection .text
bits 64
long_mode_start:; 清空所有的段寄存器,因为当前为平坦模式,不需要段选择器mov ax, 0mov ss, axmov ds, axmov es, axmov fs, axmov gs, ax; 打印 `OKAY` 到屏幕mov rax, 0x2f592f412f4b2f4fmov qword [0xb8000], raxhlt

boot.asm

section .multiboot_header
header_start:dd 0x1BADB002  ; 魔法数字,固定值dd 0dd -0x1BADB002 ; 定义的这三个数字相加需要等于0
header_end:global start
extern long_mode_start
section .text
bits 32
start:; 栈是否高地址往低地址增长mov esp, stack_topcall check_cpuidcall check_long_modecall set_up_page_tablescall enable_paginglgdt [gdt64.pointer]; 远跳指令,清空流水线,执行 64 位指令jmp gdt64.code:long_mode_start; 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 .rodata
gdt64:dq 0 ; 和之前一样,第一段为 0
.code: equ $ - gdt64 ; 需要跳转到代码段; 43 表示代码段,44 同样为 1,47 表示可用,53 表示 64 位dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; 代码段.pointer:dw $ - gdt64 - 1dq gdt64section .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/743411.html

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

相关文章

0178-开启四级分页

环境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 级分页。 关于分页的信息,需要找其它的资料另外了解,这里不做详细介绍。 介绍…

『比赛记录』【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…