阅读分析Linux0.11 /boot/head.s

news2025/4/19 23:23:41

目录

  • 初始化IDT、IDTR和GDT、GDTR
  • 检查协处理器并设置CR0寄存器
  • 初始化页表和CR3寄存器,开启分页

初始化IDT、IDTR和GDT、GDTR

startup_32:
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	mov %ax,%fs
	mov %ax,%gs
	lss _stack_start,%esp
	call setup_idt
	call setup_gdt
	movl $0x10,%eax		# reload all the segment registers
	mov %ax,%ds		# after changing gdt. CS was already
	mov %ax,%es		# reloaded in 'setup_gdt'
	mov %ax,%fs
	mov %ax,%gs
	lss _stack_start,%esp
	xorl %eax,%eax
1:	incl %eax		# check that A20 really IS enabled
	movl %eax,0x000000	# loop forever if it isn't
	cmpl %eax,0x100000
	je 1b

个人阅读上面代码的知识点:

  • lss _stack_start,%esp 首先lss是远指针加载指令,不了解的查deepseek就懂了。“_stack_start”这个标号找了很久都找不到在哪儿定义的,搜索整个源码都没有。最后还是求助deepseek终于搞懂了(deepseek真自学神器)。要把“_stack_start”中下划线去掉,直接搜索“stack_start”,才知道这个标号是定义在kernel/sched.c中的结构体变量名。之所以要加下划线,是因为早期的编译器编译时会在C中变量名前面加,现在不加了 。由于对kernel代码不懂,所以没过多研究,知道是在初始化栈就行了。
  • call setup_idt 调用子程序初始化IDT表,通过指令lidt将IDT表的长度和地址加载到IDTR寄存器。 还处在内核初始化阶段,所以只是简单的将IDT表中的描述符初始化为同一个,都指向ignore_int这个中断处理程序。ignore_int子程序就是本文件中,功能就是打印一段字符。
    • lidt指令加载的IDT表的地址是线性地址,初始化阶段,此时还没开启分页模式,线性地址等于物理地址。开启分页后,IDTR中的线性地址要经过MMU查页表转化成IDT表在内存中的物理地址。
    • 需要对IDT表描述符了解,IDT表描述符总共8字节,小端法存入内存,先存低4字节,再高4字节,先低2字节,再高2字节。之所以提小端法是因为我一开始没注意,把描述符的选择子判断错了。
  • call setup_gdt 调用子程序初始化GDT表,通过指令lgdt将GDT表的长度和地址加载到GDTR寄存器。 GDT表自己构造。
    • lgdt指令加载的GDT表的地址是线性地址,只不过初始化阶段,此时还没开启分页模式,线性地址等于物理地址。开启分页后,GDTR中的线性地址要经过MMU查页表转化成GDT表在内存中的物理地址。
    • 构造GDT表时,GDT表中的描述符大小为8字节,第一个描述符为全0,后面描述符的基地址base=0X00000000,limit=最大值(0X000FFF),把内存管理就设置成了平坦模式, 现代操作系统支持这种模式,这样一个进程的 代码段、数据段、栈段就共享同一个线性地址空间了,方便虚拟内存管理
  • 保护模式下,指令执行过程中,查询IDT表、GDT表的工作是有硬件来实现的,不是软件模拟的,所以对IDT、GDT中描述符的结构也是硬件规定的。我们在构造这两个表时要按照硬件的规定来。
  • movl %eax,0x000000;cmpl %eax,0x100000 这段是负责检查A20地址线是否打开,如果没打开那么0X100000地址就等于0X000000地址,cmpl比较的结果就是相等,那就卡在这个执行死循环。
  • je 1b 这个条件跳转指令开始看了我也很蒙,还是靠deepseek,这是以前的写法“b”表示满足条件跳转到后面的标号,“f”表示满足条件跳转到前面的标号。b和f指示跳转的方向的。现在好像不用这种用法了



检查协处理器并设置CR0寄存器

movl %cr0,%eax		# check math chip
	andl $0x80000011,%eax	# Save PG,PE,ET
/* "orl $0x10020,%eax" here for 486 might be good */
	orl $2,%eax		# set MP
	movl %eax,%cr0
	call check_x87
	jmp after_page_tables

/*
 * We depend on ET to be correct. This checks for 287/387.
 */
check_x87:
	fninit
	fstsw %ax
	cmpb $0,%al
	je 1f			/* no coprocessor: have to set bits */
	movl %cr0,%eax
	xorl $6,%eax		/* reset MP, set EM */
	movl %eax,%cr0
	ret

个人阅读上面代码的知识点:

  • 了解CR0寄存器,CR0寄存器中,PE位开启保护模式、PG位开启分页机制、WP位写保护位。还有其它涉及协处理器的位,我一知半解就不写了。上面这段代码主要就是检查协处理器,然后进行设置。 指令call check_x87就是调用子程序检查是否存在287/387协处理器。
  • fninit fstsw这两个是协处理器指令,自己查deepseek能看懂,不赘述了。check_x87这个子程序功能就是检查协处理器的。



初始化页表和CR3寄存器,开启分页

after_page_tables:
	pushl $0		# These are the parameters to main :-)
	pushl $0
	pushl $0
	pushl $L6		# return address for main, if it decides to.
	pushl $main
	jmp setup_paging
L6:
	jmp L6			# main should never return here, but
				# just in case, we know what happens.
-----------------------------------------------------------------------------------------
setup_paging:
	movl $1024*5,%ecx		/* 5 pages - pg_dir+4 page tables */
	xorl %eax,%eax
	xorl %edi,%edi			/* pg_dir is at 0x000 */
	cld;rep;stosl
	movl $pg0+7,pg_dir		/* set present bit/user r/w */
	movl $pg1+7,pg_dir+4		/*  --------- " " --------- */
	movl $pg2+7,pg_dir+8		/*  --------- " " --------- */
	movl $pg3+7,pg_dir+12		/*  --------- " " --------- */
	movl $pg3+4092,%edi
	movl $0xfff007,%eax		/*  16Mb - 4096 + 7 (r/w user,p) */
	std
1:	stosl			/* fill pages backwards - more efficient :-) */
	subl $0x1000,%eax
	jge 1b
	xorl %eax,%eax		/* pg_dir is at 0x0000 */
	movl %eax,%cr3		/* cr3 - page directory start */
	movl %cr0,%eax
	orl $0x80000000,%eax
	movl %eax,%cr0		/* set paging (PG) bit */
	ret			/* this also flushes prefetch-queue */

个人阅读上面代码的知识点:

  • jmp setup_paging 跳转到初始化页表的子程序。该指令前面的push指令是在压栈,pushl $main 目的是从setup_paging子程序返回时,ret指令能返回到main程序。 我还没看过main.c的代码,不赘述了。功能就是结束head.s的初始化任务。开启main.c
    • 强化我自己的一个知识点,call和jmp都是跳转指令,区别就是call跳转前要将返回地址压栈,jmp无需压栈直接跳转无法返回。开始我还疑问为什么不用call指令,然后返回到main.c,仔细一想call指令是将下一条指令的地址压栈,只能返回到本源文件call的下一条指令。而我们需要的是结束head.s,返回到另一个源文件main.c。所以通过pushl $main压入返回地址,不用call调用,而用jmp直接跳转到setup_paging初始化页表子程序
  • setup_paging子程序初始化页表、CR3寄存器,置CR0寄存器的PG为1开启分页。了解页表和页表项,即使有些汇编指令不熟,查一下就是看懂这段了。
    • stosl指令,我忽略了它执行时会自动增加EDI。stos、movs、cmps、scas都是字符串操作指令,可以顺便都了解一下。伟大的deepseek很好用的。
    • CR3寄存器是用来存放顶级页表的物理地址的。MMU将线性地址转换位物理地址时需要通过CR3寄存器找到页表进行映射。CR3寄存器的改变,会引起TLB表的改变。CR3改变代表页表的切换,TLB相当于部分页表项的缓存,自然也要切换



.org 0x1000
pg0:

.org 0x2000
pg1:

.org 0x3000
pg2:

.org 0x4000
pg3:

.org 0x5000
刚看见这段代码时,感觉整齐有规律,就是不知道作用是什么。即使知道了.org这个伪指令的功能,也不知道写这段代码的意义。直到最后看到初始化页表的子程序setup_paging。补充点因为这段代码学到的零碎知识点。
(1).org(Origin)是汇编语言中的一条伪指令(Directive),用于指定程序或数据在内存中的起始地址。 在需要直接控制内存布局的场景(如裸机开发、嵌入式开发)中,.org 是一个简单有效的工具,但在高层编程中通常由链接器管理地址分配
(2) 这段代码其实就是在划分页表的页。它们之间的地址差是0X1000(4KB),从0地址开始到0X5000共20KB,分成5个页,每页4KB,第一个页是页目录表,其余4个页都是普通页表。setup_paging子程序的开始将5个页20KB用0填充,接着构造4个普通物理页的页表项填充到0地址的页目录表,最后构造一个普通物理页的页表项填充4个普通物理页。
(3)你还可以观察到初始化IDT、IDTR和GDT、GDTR的代码,还有检查协处理器并设置CR0寄存器的代码都写在这段代码之前,因为那些初始化、检查的代码执行完就没用了,它们所在的内存空间可以被页表覆盖; 结束head.s跳转到main.c的代码、初始化页表的子程序setup_paging、IDT表GDT表的位置,包括中断处理程序ignore_int的代码位置都在“.org 0X5000”之后,就是防止误操作,初始化页表时覆盖比较重要的代码、表空间
(4) 内存中IDT表、GDT表,各有1个,所有应用程序和操作系统共用;页表有多个,每个应程序都有自己的页表,任务切换时,通过修改CR3寄存器切换页表,同时TLB表也要刷新。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序实现table样式,自带合并行合并列

微信小程序在代码编写过程好像不支持原生table的使用&#xff0c;在开发过程中偶尔又得需要拿table来展示。 1.table效果展示 1.wxml <view class"table-container"><view class"table"><view class"table-row"><view cla…

电脑的品牌和配置

我的笔记本是2020年买的&#xff0c;之前的订单找不到了&#xff0c;就知道是联想&#xff0c;不清楚具体的配置。 本文来源&#xff1a;腾讯元宝 检查系统信息&#xff08;Windows&#xff09; 这通常是 ​​联想&#xff08;Lenovo&#xff09;​​ 的型号代码。 81XV 是联想…

Redis面试——常用命令

一、String &#xff08;1&#xff09;设置值相关命令 1.1.1 SET 功能&#xff1a;设置一个键值对&#xff0c;如果键已存在则覆盖旧值语法&#xff1a; SET key value [EX seconds] [PX milliseconds] [NX|XX]EX seconds&#xff1a;设置键的过期时间为 seconds 秒 PX milli…

Swin-Transformer-UNet改进:融合Global-Local Spatial Attention (GLSA) 模块详解

目录 1.模块概述 2.swinUNet网络 3. 完整代码 1.模块概述 Global-Local Spatial Attention (GLSA) 是一种先进的注意力机制模块,专为计算机视觉任务设计,能够同时捕捉全局上下文信息和局部细节特征。 该模块通过创新的双分支结构和自适应融合机制,显著提升了特征表示能…

ubuntu 向右拖动窗口后消失了、找不到了

这是目前单显示器的设置&#xff0c;因为实际只有1个显示器&#xff0c;之前的设置如下图所示&#xff0c;有2个显示器&#xff0c;一个主显示器&#xff0c;一个23寸的显示器 ubuntu 22.04 系统 今天在操作窗口时&#xff0c;向右一滑&#xff0c;发现这个窗口再也不显示了、找…

2025最新版微软GraphRAG 2.0.0本地部署教程:基于Ollama快速构建知识图谱

一、前言 微软近期发布了知识图谱工具 GraphRAG 2.0.0&#xff0c;支持基于本地大模型&#xff08;Ollama&#xff09;快速构建知识图谱&#xff0c;显著提升了RAG&#xff08;检索增强生成&#xff09;的效果。本文手把手教你如何从零部署&#xff0c;并附踩坑记录和性能实测…

libevent服务器附带qt界面开发(附带源码)

本章是入门章节&#xff0c;讲解如何实现一个附带界面的服务器&#xff0c;后续会完善与优化 使用qt编译libevent源码演示视频qt的一些知识 1.主要功能有登录界面 2.基于libevent实现的服务器的业务功能 使用qt编译libevent 下载这个&#xff0c;其他版本也可以 主要是github上…

智能体数据分析

数据概览&#xff1a; 展示智能体的累计对话次数、累计对话用户数、对话满意度、累计曝光次数。数据分析&#xff1a; 统计对话分析、流量分析、用户分析、行为分析数据指标&#xff0c;帮助开发者完成精准的全面分析。 ps&#xff1a;数据T1更新&#xff0c;当日12点更新前一天…

STM32(M4)入门: 概述、keil5安装与模板建立(价值 3w + 的嵌入式开发指南)

前言&#xff1a;本教程内容源自信盈达教培资料&#xff0c;价值3w&#xff0c;使用的是信盈达的405开发版&#xff0c;涵盖面很广&#xff0c;流程清晰&#xff0c;学完保证能从新手入门到小高手&#xff0c;软件方面可以无基础学习&#xff0c;硬件学习支持两种模式&#xff…

采用若依vue 快速开发系统功能模块

文章目录 运行若依项目 科室管理科室查询-后端代码实现科室查询-前端代码实现科室名称状态搜索科室删除-后端代码实现科室删除-前端代码实现科室新增-后端代码实现科室新增-前端代码实现科室修改-后端代码实现前端代码实现角色权限实现 运行若依项目 运行redis 创建数据库 修改…

HTML:表格数据展示区

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>人员信息表</title><link rel"styl…

Oracle测试题目及笔记(单选)

所有题目来自于互联网搜索 当 Oracle 服务器启动时&#xff0c;下列哪种文件不是必须的&#xff08;D&#xff09;。 A&#xff0e;数据文件 B&#xff0e;控制文件 C&#xff0e;日志文件 D&#xff0e;归档日志文件 数据文件、日志文件-在数据库的打开阶段使用 控制文件-在数…

Jmeter创建使用变量——能够递增递减的计数器

Jmeter创建使用变量——能够递增递减的计数器 如下图所示&#xff0c;创建一个 取值需限定为0 2 4这三个值内的变量。 Increment&#xff1a;每次迭代后 递增的值&#xff0c;给计数器增加的值 Maximum value&#xff1a;计数器的最大值&#xff0c;如果超过最大值&#xff0…

数据结构之BFS广度优先算法(腐烂的苹果)

队列这个数据结构在很多场景下都有使用&#xff0c;比如在实现二叉树的层序遍历&#xff0c;floodfill问题(等等未完成)中&#xff0c;都需要借助队列的先进先出特性&#xff0c;下面给出这几个问题的解法 经典的二叉树的层序遍历 算法图示&#xff0c;以下图所示的二叉树为例…

火车头采集动态加载Ajax数据(无分页瀑布流网站)

为了先填充好数据在上线&#xff0c;在本地搭建了一个网站&#xff0c;并用火车头采集数据填充到里面。 开始很上手&#xff0c;因为找的网站的分类中是有分页的。很快捷的找到页面标识。 但是问题来了&#xff0c;如今很多网站都是采用的Ajax加载数据&#xff0c;根本没有分…

Node.js模块化与npm

目录 一、模块化简介 二、CommonJS 规范 1. 基本语法 2. 导出模块 3. 导入模块 三、ECMAScript 标准&#xff08;ESM&#xff09; 1. 启用 ESM 一、默认导出与导入 1. 基本语法 2. 默认导出&#xff08;每个模块仅一个&#xff09; 3. 默认导入 二、命名导出与导入…

nginx中的代理缓存

1.缓存存放路径 对key取哈希值之后&#xff0c;设置cache内容&#xff0c;然后得到的哈希值的倒数第一位作为第一个子目录&#xff0c;倒数第三位和倒数第二位组成的字符串作为第二个子目录&#xff0c;如图。 proxy_cache_path /xxxx/ levels1:2 2.文件名哈希值

【前端vue生成二维码和条形码——MQ】

前端vue生成二维码和条形码——MQ 前端vue生成二维码和条形码——MQ一、安装所需要的库1、安装qrcode2、安装jsbarcode 二、使用步骤1、二维码生成2、条形码生成 至此&#xff0c;大功告成&#xff01; 前端vue生成二维码和条形码——MQ 一、安装所需要的库 1、安装qrcode 1…

flutter 桌面应用之窗口自定义

在开发桌面软件的时候我们经常需要配置软件的窗口的大小以及位置 我们有两个框架选择:window_manager和bitsdojo_window 对比bitsdojo_window 特性bitsdojo_windowwindow_manager自定义标题栏✅ 支持❌ 不支持控制窗口行为&#xff08;大小/位置&#xff09;✅&#xff08;基本…

华为OD机试真题——MELON的难题(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析&#xff1b; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式&#xff01; 2025华为OD真题目录全流程解析/备考攻略/经验分享 华为OD机试真题《MELON的…