Linux 0.00 代码解析(一)

未分类 同时被 2 个专栏收录
8 篇文章 0 订阅

《Linux内核完全剖析》这本书在第4章给出了一个简单多任务内核示例程序,作者称之为Linux 0.00系统。

源码的下载地址和实验方法可以参考我的博文
http://blog.csdn.net/longintchar/article/details/78757065

本文想分析一下启动代码boot.s.

boot.s,采用as86语言编写,是引导启动程序,其作用是把内核代码加载到内存0x10000处,之后设置好临时GDT表等信息,再把处理器设置成保护模式,最后跳转到内核代码处运行。

我打算边贴代码边分析。如有纰缪,还请各位看客拍砖指教。

BOOTSEG = 0x07c0
SYSSEG  = 0x1000            ! system loaded at 0x10000 (65536).
SYSLEN  = 17                ! sectors occupied.

以上三行都是在定义符号常量,也就是说BOOTSEG 就是0x07c0. 属于伪指令,并不会分配内存。

entry start
start:
    jmpi    go,#BOOTSEG
go: mov ax,cs
    mov ds,ax
    mov ss,ax
    mov sp,#0x400       

标识符entry是保留关键字,用于迫使链接器ld86在生成的可执行文件中包括其后指定的标号。通常在链接多个目标文件生成一个可执行文件时应该在其中一个汇编程序中用关键词entry指定一个入口标号,以便调试。但是这里可以省略entry start,因为我们不希望在生成的纯二进制文件中包括任何符号信息。

jmpi go,#BOOTSEG,是一个段间跳转语句,跳转到0x07c0:go处。当BIOS把主引导扇区(也就是boot.s生成的二进制镜像)加载到物理内存0x7c00处并跳转到该处时,所有段寄存器(包括CS)的默认值均为0,即此时CS:IP=0x0000:0x7c00。因此这里使用段间跳转语句就是为了给CS赋值0x07c0。该语句执行后CS:IP = 0x07C0:go

load_system:
    mov dx,#0x0000
    mov cx,#0x0002
    mov ax,#SYSSEG
    mov es,ax
    xor bx,bx
    mov ax,#0x200+SYSLEN
    int     0x13
    jnc ok_load
die:    jmp die

INT 13H,AH=02H 读扇区
此功能从磁盘上把一个或更多的扇区内容读进内存。因为这是一个低级功能,在一个操作中读取的全部扇区必须在同一条磁道上。

入口参数
AH=02H ,指明调用读扇区功能。
AL要读的扇区数目,不允许使用读磁道末端以外的数值,也不允许使该寄存器为0。
DL需要进行读操作的驱动器号,0表示软盘,80H表示硬盘。
DH所读磁盘的磁头号。
CH磁道号的低8位数(磁道号共10位)。
CL低5位放入所读起始扇区号,位7-6表示磁道号的高2位。
ES:BX读出数据的缓冲区地址。
返回参数
CF=1,则操作失败;=0,操作成功。
AH错误返回码。
AL实际读到的扇区数。

所以,对照代码,可以得出从软盘的0磁头,0磁道,从第2个扇区起,连续读17个扇区到0x1000:0x0000(即0x10000)处。最后两行表示判断CF标志,如果标志置位说明出错,则陷入死循环。否则跳转到ok_load处。

ok_load:
    cli         ! no interrupts allowed !
    mov ax, #SYSSEG
    mov ds, ax  !ds=0x1000
    xor ax, ax
    mov es, ax  !es=0
    mov cx, #0x2000 !书上是0x1000
    sub si,si   !si=0
    sub di,di   !di=0
    rep
    movw   !每次移动一个字

上面的代码表示把 DS:SI(0x1000:0x0)处的内容移动到ES:DI(0x0:0x0); CX 中是重复的次数(按0x1000算,就是4K),每次移动一个字(2B),所以一共移动了8KB(内核的长度不超过8KB)的代码。

    lidt    idt_48      ! load idt with 0,0
    lgdt    gdt_48      ! load gdt with whatever appropriate

! absolute address 0x00000, in 32-bit protected mode.
    mov ax,#0x0001  ! protected mode (PE) bit
    lmsw    ax      ! This is it!
    jmpi    0,8     ! jmp offset 0 of segment 8 (cs)

gdt:    .word   0,0,0,0     ! dummy

    .word   0x07FF      ! 8Mb - limit=2047 (2048*4096=8Mb)
    .word   0x0000      ! base address=0x00000
    .word   0x9A00      ! code read/exec
    .word   0x00C0      ! granularity=4096, 386

    .word   0x07FF      ! 8Mb - limit=2047 (2048*4096=8Mb)
    .word   0x0000      ! base address=0x00000
    .word   0x9200      ! data read/write
    .word   0x00C0      ! granularity=4096, 386

idt_48: .word   0       ! idt limit=0
    .word   0,0     ! idt base=0L
gdt_48: .word   0x7ff       ! gdt limit=2048, 256 GDT entries
    .word   0x7c00+gdt,0    ! gdt base = 07xxx

lidt是加载IDTR(中断描述符表寄存器),其后面要跟一个内存地址。在16位模式下,该地址是16位的;在32位模式下,该地址是32位的。该指令在实模式和保护模式下都可以用。地址指向一个6字节的内存区域,前16位是IDT的界限值,后32位是IDT的线性地址。

lgdt是加载GDTR(全局描述符表寄存器),用法与lidt类似。gdt_48处定义了6个字节,前两个字节表示表的界限,0x7FF+1=2048(十进制),2048/8 = 256,也就是最多可以容纳256个段描述符;后四个字节是GDT的线性基地址,0x7c00+gdt,因为主引导扇区被BIOS加载到了0x7c00处,所以要加个偏移。

9到19行定义了3个段描述符,第0个不用;

关于数据段描述符和代码段描述符,可以参考我的博文:
http://blog.csdn.net/longintchar/article/details/50489889

第1个描述符定义了一个代码段,其基地址为0,界限值是0x7FF(10进制2047),粒度4KB,DPL=0,非一致性,可读可执行。因为粒度是4KB,所以段长度是(2047+1)*4KB=8MB。

第2个描述符定义了一个数据段,其基地址为0,界限值是0x7FF(10进制2047),粒度4KB,DPL=0,向上扩展,可读可写。同上,段长度是8MB。

再来解释5~7行。
lmsw是加载机器状态字指令,后接16位寄存器或者内存地址。其功能是用源操作数的低4位加载CR0,也就是说仅会影响CR0的低4位——PE, MP, EM, TS。

这里写图片描述

第6行执行完成后,保护模式就开启了。

即使在实模式下,段寄存器的高速缓存寄存器也被用于访问内存。当处理器进入保护模式后,高速缓存寄存器的内容依然残留,但是这些内容在保护模式下是无效的。因此,比较安全的做法是尽快刷新段选择器,包括描述符高速缓存寄存器。

另外,在进入保护模式之前,很多指令已经进入了流水线。因为处理器工作在实模式下,所以它们都是按照16位操作数和地址长度进行译码的,即使是那些用bits32编译的指令,为了防止执行结果不正确,所以必须清空流水线。还用,那些通过乱序执行得到的中间结果也是无效的,所以必须清理掉,让处理器串化执行。

为了达到上述目的,我们可以采用段间跳转指令。jmpi 0,8执行后,处理器一般会清空流水线并且串化执行;另一方面,会重新加载CS,并刷新描述符高速缓存寄存器的内容。

jmpi 0,8,这里的0是偏移地址,8是段选择子。段选择子的结构如下图:

这里写图片描述

TI=0表示描述符在GDT中,TI=1表示描述符在LDT中。描述符索引则表示第几个描述符(从0开始)。

8即二进制的1000,也就是说是GDT表的第1个描述符,即基地址为0的代码段。基地址0+偏移地址0=0,所以jmpi 0,8表示跳转到物理地址0处,这正是内核代码的起始位置,此后内核开始执行了。

源码的最后两行是

.org 510
    .word   0xAA55

伪指令.org 510表示在它之后的指令从地址510开始存放。遇到.org,编译器会把其后的指令代码放到org伪指令指定的偏移地址。如org指定的地址和之前的指令地址有空洞,则用0填充。

.word 0xAA55是有效引导扇区的标志,第510字节必须是0x55,第511字节必须是0xAA.

【参考资料】
《Linux内核完全剖析》

  • 5
    点赞
  • 0
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
第1章 Linux 简介让用户很详细地了解大多数现有操作系统的实际工作方式是不可能的,因为大多数操作系统的源代码都是严格保密的。除了些研究用的及为操作系统教学而设计的系统外。尽管研究和教学目的都很好,但是这类系统很少能够通过对正式操作系统的小部分实现来体现操作系统的实际功能。对于操作系统的些特殊问题,这种折衷系统所能够表现的就更是少得可怜了。在以实际使用为目标的操作系统中,让任何人都可以自由获取系统源代码,无论目的是要了解、学习还是改进,这样的现实系统并不多。本书的主题就是这些少数操作系统中的个:LinuxLinux的工作方式类似于Uinx,它是免费的,源代码也是开放的,符合标准规范的32位在64位CPU上是64位操作系统。Linux拥有现代操作系统的所具有的内容,例如:?真正的抢先式多任务处理,支持多用户。?内存保护。?虚拟内存。?支持对称多处理机SMPsymmetric multiprocessing,即多个CPU机器以及通常的单CPUUP机器。?符合POSIX标准。?联网。?图形用户接口和桌面环境实际上桌面环境并不只。?速度和稳定性。严格说来,Linux并不是个完整的操作系统。当我们在安装通常所说的Linux时,我们实际安装的是很多工具的集合。这些工具协同工作以组成个功能强大的实用系统。Linux本身只是这个操作系统的内核,是操作系统的心脏、灵魂、指挥中心整个系统应该称为GNU/Linux,其原因在本章的后续内容中将会给以介绍。内核以独占的方式执行最底层任务,保证系统正常运行—协调多个并发进程,管理进程使用的内存,使它们相互之间不产生冲突,满足进程访问磁盘的请求等等。在本书中,我们给大家揭示的就是Linux是如何完成这具有挑战性的工作的。1.1 Linux和Unix的简明历史为了让大家对本书所讨论的内容有更清楚的了解,让我们先来简要回顾Linux的历史。由于Linux是在Unix的基础上发展而来的,我们的话题就从Unix开始。Unix是由AT&T贝尔实验室的Ken Thompson和Dennis Ritchie于1969年在台已经废弃了的PDP-7上开发的;它最初是个用汇编语言写成的单用户操作系统。不久,Thompson和Ritchie成功地说服管理部门为他们购买更新的机器,以便该开发小组可以实现个文本处理系统,Unix就在PDP-11上用C语言重新编写发明C语言的部分目的就在于此。它果真变成了个文本处理系统—不久之后。只不过问题是他们先实现了个操作系统而已……最终,他们实现了该文本处理工具,而且Unix以及Unix上运行的工具也在AT&T得到广泛应用。在1973年,Thompson和Ritchie在个操作系统会议上就这个系统发表了篇论文,该论文引起了学术界对Unix系统的极大兴趣。由于1956年反托拉斯法案的限制,AT&T不能涉足计算机业务,但允许它象征性地收取费用发售该系统。就这样,Unix被广泛发布,首先是学术科研用户,后来又扩展到政府和商业用户。伯克利加州大学是学术用户中的个。在这里,Unix得到了计算机系统研究小组CSRG的广泛应用。并且在这里所进行的修改引发了Unix的大系列,这就是广为人知的伯克利软件开发BSDUnix。除了AT&T所提供的Unix系列之外,BSD是最有影响力的Unix系列。BSD在Unix中增加了很多显著特性,例如TCP/IP网络,更好的用户文件系统UFS,工作控制,并且改进了AT&T的内存管理代码。多年以来,BSD版本的Unix直在学术环境中占据主导地位,但最终发展成为System V版本的AT&T的Unix则成为商业领域的领头羊。从某种程度上来说,这是有社会原因的:学校倾向于使用非正式但通常更好用的BSD风格的Unix,而商业界则倾向于从AT&T获取Unix。在用户需求和用户编程改进特性的促进下,BSD风格的Unix般要比AT&T的Unix更具有创新性,而且改进也更为迅速。但是,在AT&T发布最后个正式版本System V Release 4SVR4时,System V Unix已经吸收了BSD的大多数重要的优点,并且还增加了些自己的优势。这部分由于从1984年开始,AT&T逐渐可以将Unix商业化,而伯克利Unix的开发工作在1993年BSD4.4版本完成以后就逐渐收缩,以至终止了。然而,BSD的进步改进由外界开发者延续下来,到今天还在继续进行。正在进行的Unix系列开发中至少有四个独立的版本是直接起源于BSD4.4,这还不包括几个厂商的Unix版本,例如惠普的HP-UX,都是部分地或者全部基于BSD而发展起来的。实际上Unix的变种并不止BSD和System V。由于Unix主要
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值