《Linux0.11源码解读》理解(四) head之重新设置IDT/GDT

2023-11-12

上节提到,现在cs:ip指向0地址,此处存储着作为操作系统核心代码的system模块,是由head.s和 main.c以及后面所有源代码文件编译链接而成。head.s(以下简称head)紧挨着main.c,我们先执行head。

重新设置内核栈

_pg_dir:
_startup_32:
    mov eax,0x10
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    lss esp,_stack_start

标号 _pg_dir表示页目录,意为在设置分页机制时,页目录会存放在这里,也会覆盖这里的代码。setup.s(以下简称setup)已经设置了gdt,现在要对段描述符重新设置包括ds/es/fs/gs。都被设置为0x10(00010000),在保护模式下即段选择子为2,指向数据段描述符。根据我们之前gdt表的内容,数据段的基地址是0,于是ds/es/fs/gs的基地址也是0。

lss 指令相当于让 ss:esp 这个栈顶指针(esp是sp的32为扩展),指向了 _stack_start 这个标号的位置(对比lds mem,reg:将段描述符mem的高位存储在 reg 寄存器的高位,而段描述符的低位存储在ds寄存器的低位)。当然之前在bootsec所设置的栈顶0x9ff00位置现在变成了0:stack_start:

// include/linux/mm.h
#define PAGE_SIZE 4096

// kernel/sched.c
long user_stack [ PAGE_SIZE>>2 ] ;
struct {
	long * a;
	short b;
	} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };

其实从第三节得知我们已经在setup的内存(位于0x90200)设置了idt、gdt。现在则通过call setup_idt和setup_gdt重新设置位于head的内存(位于0x90000)的idt、gdt。为何重复设置?

因为位于setup的内存会在将来设计缓冲区时被覆盖,而且也不能将setup中的idt和gdt直接copy到现在的位置(在执行setup的时候copy无意义,因为如果先执行setup后移动system会覆盖掉copy的idt、gdt;如果先移动system后执行setup则会覆盖掉head内容),于是我们不得不在head重新设置它们。

设置IDT

即便是setup里面的idt也都是空的,现在由head程序正式设置。

setup_idt:
    lea edx,ignore_int  ;lea将ignore_int偏移地址(16bit)/而mov将第二操作数的内存内容 放入edx
    mov eax,00080000h   ;将段选择子0x0008置入eax高16位
    mov ax,dx           ;将ignore_int偏移地址置入eax低16位
    mov dx,8E00h        ;interrupt gate - dpl=0, present
    lea edi,_idt        ;lea将_idt所代表偏移地址放入edi
    mov ecx,256         ;cx用来计数,256次
rp_sidt:
    mov [edi],eax       ;[]寄存器间接寻址,表示eax的内容赋予“以edi的内容作为地址指针的”内存。
    mov [edi+4],edx
    add edi,8
    dec ecx
    jne rp_sidt
    lidt fword ptr idt_descr   ;fword ptr是48位指针,用于远程跳转
    ret

idt_descr:
    dw 256*8-1    ;db字节(1 byte)类型,dw字类型(2 byte),dd双字类型(4 byte)
    dd _idt

_idt:
    DQ 256 dup(0) ;伪操作,用来定义操作数占用的字节数

ignore_int作为默认中断处理程序函数地址,会放入中断描述符内。中段描述符结构如下:

这段代码意为将eax作为低32bit、edx作为高32bit填充一个中断描述符,并以cx作为计数器一共填充256次(共256项),以此来初始化整个IDT。最后通过lidt加载中断描述符至idtr让cpu识别。

重新设置GDT

setup_gdt:
	lgdt gdt_descr
	ret
...
.align 2
.word 0
gdt_descr:
	.word 256*8-1		; gdtr内容是gdt的界限, 以及gdt所在的地址
	.long _gdt		    ; 每个gdt项占8byte, 一共256个gdt项, gdt总量2048byte

.align 3
_gdt:	.quad 0x0000000000000000;   ;.quad为4word/8byte(等同.word 0,0,0,0). NULL desp
	    .quad 0x00c09a0000000fff	; 代码段, 0fff=>4096, 4096*4096=16Mb
	    .quad 0x00c0920000000fff	; 数据段, 除了基地址以外,其他同上
	    .quad 0x0000000000000000	; TEMPORARY - don't us
	    .fill 252,8,0			    ; space for LDT's and TSS's etc

对照gdt项所设置内容0x00c09a0000000fff的二进制和全局描述符格式:


g(granularity)粒度位如为0,段限长以1字节为单元;为1,段限长以4K字节为单元;dpl描述符特权级0和3级;p段存在位,该位为1指示描述符存在。于是0x00c09a00表示g为1,p为1,那么此代码段限长为4096*4K=16M。

重置了idt/gdt,接着又重新执行了一遍刚刚执行过的代码。为什么要重新设置这些段寄存器呢?因为修改了 gdt,所以要重新设置一遍,做个刷新,这样修改才能生效。

call setup_idt ;设置中断描述符表
call setup_gdt ;设置全局描述符表
mov eax,10h
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
lss esp,_stack_start

检验A20地址线是否打开

需要检验A20地址线是否打开,因为这会影响保护模式是否有效。这里通过如果没打开A20则0x100000会回滚到0x000000来判断,并不断循环直到A20开启为止:

	xor eax,eax         ; 异或,清空eax
1:	inc eax		        ; check that A20 really IS enabled
	mov 0x000000,eax	; loop forever if it isn't
	cmp 0x100000,eax
	je 1b               ; zf为0则跳转(通常搭配cmp,如源操作数和目标操作数相等,则跳转)

在检测到保护模式有效后,如果是486之前的cpu,会配备数学协处理器芯片以增强浮点计算能力。大概是先检查数学协处理器芯片是否存在。方法是修改控制寄存器CR0,在假设协处理器存在的情况下执行一个协处理器指令,如果出错的话则说明协处理器芯片不存在。这段代码不贴出来了,详细参见:flash-linux0.11-talk/head.s at main · dibingfa/flash-linux0.11-talk · GitHub

 开启分页机制,为进入main函数做准备

设置完协处理器后,将要开启分页机制,这是head的最后阶段也是执行main函数前的最后阶段。

...
	jmp after_page_tables
...
after_page_tables:
    push 0
    push 0
    push 0
    push L6
    push _main
    jmp setup_paging
L6:
    jmp L6

可以看到将main函数参数、L6以及main函数地址都压栈,然后跳转到设置分页的标号。这些压栈是为了开启分页后执行main函数。

此外,即便main函数退出,程序也不会结束,因为我们看到程序到L6这边,是个死循环。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《Linux0.11源码解读》理解(四) head之重新设置IDT/GDT 的相关文章

  • 从命名管道读取

    我必须实现一个 打印服务器 我有 1 个客户端文件和 1 个服务器文件 include
  • libusb 和轮询/选择

    我正在使用 Linux 操作系统 想知道是否有任何文件描述符可以轮询 选择 当数据等待从 USB 设备读取时会触发这些文件描述符 我也在使用 libusb 库 但尚未找到可以使用的文件描述符 Use libusb 的轮询函数 http li
  • 命令行参数中的“-”(破折号)有什么魔力?

    例子 创建 ISO 映像并将其直接刻录到 CD mkisofs V Photos r home vivek photos cdrecord v dev dev dvdrw 更改到上一个目录 cd 侦听端口 12345 并解压发送到该端口的数
  • 让“git pull”在拉取不同分支时要求确认

    当同时处理许多项目和分支时 我偶尔会犯一些愚蠢的错误 比如拉入错误的分支 例如在分支上master I did git pull origin dangerous code并且有一段时间没有注意到这一点 这个小错误造成了很大的混乱 当我尝试
  • 在 Linux 2.6.21 (glibc 2.3.5) 上进行 ARP 和反向 ARP

    我需要在任意 IP 网络上存储对第三方设备的持久引用 其中设备的 IP 地址可能是静态的或由 DHCP 随机分配 我不控制网络上的设备 也不能依赖 DNS 和其他现有的或与设备一起使用的临时网络协议 所以我被指示使用硬件地址和 ARP 进行
  • 如何修改s_client的代码?

    我正在玩apps s client c in the openssl源代码 我想进行一些更改并运行它 但是在保存文件并执行操作后 我的更改没有得到反映make all or a make 例如 我改变了sc usage函数为此 BIO pr
  • 在 C 中使用 sqrtf():“未定义对‘sqrtf’的引用”

    我正在使用Linux Ubuntu 12 04 https en wikipedia org wiki Ubuntu version history Ubuntu 12 04 LTS 28Precise Pangolin 29 Precis
  • 当我通过 shell 脚本创建 .txt 文件时,为什么文件名末尾出现问号? [复制]

    这个问题在这里已经有答案了 我正在编写一个 shell 脚本 我应该在其中创建 1 个文本文件 当我这样做时 文件名末尾出现一个问号 是什么原因 我正在 bash 脚本中尝试以下方法 1 grep ERROR a1 gt text txt
  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4
  • Bash 中 $() 和 () 之间的区别

    当我打字时ls l echo file 支架的输出 这只是简单的回显 被获取并传递到外部ls l命令 就等于简单的ls l file 当我打字时ls l echo file 我们有错误 因为不能嵌套 内部外部命令 有人可以帮助我理解之间的区
  • 有关 Linux 内存类型的问题

    关于Linux内存我有以下问题 我知道活动内存是最常访问的内存部分 但是有人可以解释一下 linux 如何考虑将内存位置用于活动内存或非活动内存 主动存储器由哪些部分组成 磁盘 文件缓存是否被视为活动内存的一部分 有什么区别Buffers
  • 无法为 Python 3.4 创建工作虚拟环境

    I 安装Python 3 4 2 https docs python org 3 using unix html building python和我的 Linux Mint 17 1 中的 Virtualenv 12 0 5 然后我尝试创建
  • 我在哪里可以学习如何使 C++ 程序与操作系统 (Linux) 交互

    我是一个 C 初学者 我想创建与操作系统交互的小程序 使用 Kubuntu Linux 到目前为止 我还没有找到任何教程或手册来让 C 与操作系统交互 在 PHP 中 我可以使用命令 exec 或反引号运算符来启动通常在控制台中执行的命令
  • 对 sf:: 的未定义引用

    我想用 C 制作 GUI 应用程序 发现 SFML 是一个不错的选择 幸运的是 我使用的是 Linux 所以 SFML 2 4 已经安装在我的系统上 所以我开始搜索一些教程并找到了一个制作简单窗口的教程 但是当我运行代码时 出现错误 提示未
  • 使用 sed 将 old-link-url 替换为 new-link-url

    我正在 bash 中编写一个脚本 将 old link url 替换为 new link url 我的问题是 sed 由于斜杠而无法替换 url 如果我只输入一些文字就可以了 my code sed e s old link new lin
  • 原生 Linux 应用程序可像 ResHacker 一样编辑 Win32 PE

    我想运行自动修改 dll服务 用户提交特定的 dll 我在服务器上修改它 然后用户可以下载 dll的修改版本 是否有任何本机 Linux 应用程序提供常见的 Win32 PE 修改功能 例如图标 字符串 加速器 对话等 至少提供命令行或脚本
  • 静态链接共享对象?或者损坏的文件?

    我有一个从专有来源获得的库 我正在尝试链接它 但出现以下错误 libxxx so 文件无法识别 文件格式无法识别 Collect2 ld 返回 1 退出状态 确实 ldd libxxx so statically linked 这究竟意味着
  • 如何从“git log”中查看 Git 中的特定版本?

    My git log显示为 enter code here git trial git log commit 4c5bc66ae50780cf8dcaf032da98422aea6e2cf7 Author king lt email pro
  • 如何仅将整个嵌套目录中的头文件复制到另一个目录,在复制到新文件夹后保持相同的层次结构

    我有一个目录 其中有很多头文件 h 和其他 o 和 c 文件以及其他文件 这个目录里面有很多嵌套的目录 我只想将头文件复制到一个单独的目录 并在新目录中保留相同的结构 cp rf oldDirectory newDirectory将复制所有
  • Windows 与 Linux 文本文件读取

    问题是 我最近从 Windows 切换到 Ubuntu 我的一些用于分析数据文件的 python 脚本给了我错误 我不确定如何正确解决 我当前仪器的数据文件输出如下 Header 有关仪器等的各种信息 Data 状态 代码 温度 字段等 0

随机推荐

  • python 如何编写一个自己的包

    python 如何编写一个自己的包 先写function 内容 package wadepypk ls init py f1 py f2 py f1 py def show print in pkg f show f2 py def sho
  • unix bsd linux gun   粗略解释

    最早的unix是开放的 很多组织对unix都有修改 期中比较有名的就是伯克利大学的修改版本 叫做bsd 是unix的分支 由于bsd的协议允许你直接使用 修改他的代码 并且可以作为商业用途 所以很多公司的unix都是从bsd衍生过来的 比如
  • mongodb学习笔记一:mongodb安装与介绍

    一 前言 最近开始学习非关系型数据库MongoDB 却在博客园上找不到比较系统的教程 很多资料都要去查阅英文网站 效率比较低下 本人不才 借着自学的机会把心得体会都记录下来 方便感兴趣的童鞋分享讨论 部分资源出自其他博客 旨将零散知识点集中
  • C/C++函数模板template

    1 说明 当函数处理功能相似 函数名相同 但是参数个数或者类型有区别 我们知道实现的方式是依靠函数重载 overload 但是如果仅函数参数或返回数的类型不同 我们想到靠函数模板解决这个问题 不仅节省内存 而且不用复杂声明多个函数 函数模板
  • 为线程池中的每个线程设置UncaughtExceptionHandler

    参考了 java并发编程实战 P134内容 每当线程池需要创建一个线程时 都是通过调用线程工厂方法来完成的 默认的线程工厂方法将创建一个新的 非守护的线程 并且不包好特殊的配置信息 如果你希望在线程运行之前 之后 或者运行中如果发生异常等情
  • Linux 系统 lscpu 命令详解

    文章目录 前言 lscpu 命令详解 命令 1 查看物理 CPU 个数 2 查看每个物理 CPU 核数 3 查看总线程数 4 查看内存信息 5 查看 linux 系统版本 前言 Linux 系统查看系统相关信息方法很多 以下详细介绍 lsc
  • 一颗二叉树代码(图解)

    什么是二叉树 树结构是一种非线性存储结构 存储的是具有一对多关系的数据的集合 而树形结构的一种抽象出来的数据结构往往是二叉树的形式 满足以下两个条件的树就是二叉树 本身是有序树 树中包含的各个节点的度不能超过 2即只能是 0或者1 或者 2
  • 项目时间管理-架构真题(二十四)

    1 霍尔提出了系统方法的三维结构体系 通常称为霍尔三维结构 这就是系统工程方法论的基础 霍尔三维结构以时间堆 堆 知识堆组成的立体结构概括性表示出系统工程在各阶段 各步骤以及所涉及的知识范围 其中时间维是系统的工作进程 对于一个具体的工程项
  • 计算机网络面试八股文攻略(二)—— TCP 与 UDP

    一 基础概念 TCP 与 UDP 是活跃于 运输层 的数据传输协议 TCP 传输控制协议 Transmission Control Protocol 提供面向连接的 可靠的数据传输服务 具体来说就是一种要建立双端连接才能发送数据 能确保传输
  • react拖拽排序、js列表拖拽

    列表拖动排序的解决方案有多种 个人感觉最简单的就是zent 提供的 Sortable 组件了 一 zent Sortable 拖拽排序 推荐 简单明了 zent 官网 https zent contrib gitee io zent zh
  • 1139: 输出最短字符串java

    import java util Scanner public class Main public static void main String args Scanner input new Scanner System in int n
  • Qt第六章 多窗口编程

    一 QMessageBox 消息对话框 掌握 QMessageBox继承自QDialog 是一个Qt内置的用来展示信息或询问用户一个问题的模态对话框 预设了四种类型 像那些已经写好的窗口 这些现成的东西都会有一些特性 就是他们的对象都不需要
  • 基于51单片机的时钟设计

    今天小刚做了一个基于51单片机的时钟 本来想把数码管显示动态扫描放到定时器1的中断里到但是 一按按键 就卡住了 效果不是很理想 所以就放弃了这种方案 不过最后也实现了功能 以下是程序代码 4个按键功能 1 切换 2 时间 3 时间 4 清零
  • [RN] windows7 安装 Realm Studio 后,打开报错 A JavaScript error occurred in the main process...

    windows7 安装 Realm Studio 后 打开报错 报错如下 A JavaScript error occurred in the main process Uncaught Exception Error The specif
  • 为什么说Java只有值传递

    为什么说Java只有值传递 1 值传递概念 2 引用传递概念 3 Java只有值传递 没有引用传递 1 值传递概念 方法调用时 会创建副本 传递的是值的副本 也就是说传递后就不相关了 2 引用传递概念 方法调用时 不会创建副本 传递的是引用
  • 树莓派内核编译

    一 概述 树莓派的github主页 https github com raspberrypi 里面包含了linux源码 交叉编译工具链等内容 对于我们要用到的有两个仓库 https github com raspberrypi linux
  • QT笔记-QTableWidget点击表格头,显示菜单项

    1 添加控件 2 示例源码 h private slots void OnClickHeader int head void OnClickMenu QAction action cpp void Textdemo OnInitTableW
  • [css3] 动画案例---会呼吸的圆

  • Python 源代码缩进格式化工具

    前言 昨天在跟小伙伴聊天 当他谈起自己正在做的项目时 一脸愁容 他吐槽道 该项目的 Python 代码库由多个人共同维护 由于每个人使用的编辑器不同 每个人的编码风格也不同 最终导致了 代码的缩进千奇百怪 有缩进 2 个空格的 有缩进 4
  • 《Linux0.11源码解读》理解(四) head之重新设置IDT/GDT

    上节提到 现在cs ip指向0地址 此处存储着作为操作系统核心代码的system模块 是由head s和 main c以及后面所有源代码文件编译链接而成 head s 以下简称head 紧挨着main c 我们先执行head 重新设置内核栈