linux 启动引导过程_bootsect

从零开始编写操作系统笔记

Lessn01 0x01

Bootstraping


阅读本文需要一些基本汇编代码知识, 不过本文中尽量将汇编代指令做好注释:
代码托管:
码云: https://gitee.com/muzi_since/Luinx0.1
github: https://github.com/cythinamissTack/neu-os

BIOS (放在RAM 中)

  • Memory mapping 内存映射
  • Power on self Test 开机自检
  • Find MBR Sector 查找启动介质(可以设置启动顺序)

CPU Register – Init state

MBR Sector – 0x07c0 (SEG)

  • Load system into RAM
  • Do Some initialization
  • Jump to the system code
操作系统引导过程:

BIOS (Basic Input & Output system)是放在主板固定的RAM中, 开机上电的时刻, BIOS代码会自动映射到内存中 此时CPU的指令寄存器会指向BIOS的映射内存地址入口代码

执行BIOS代码 如开机自检等

查找启动介质 通过设置的查找顺序, 一个一个查找 (筛查的依据是是否已"55AA"结束;

一旦找到了启动引导介质, 就会自动将介质中的代码主引导记录加载内存地址的 0x7c00

在这里插入图片描述

至此, BIOS完成了装在引导扇区到内存的启动默认地址的工作

接下来就是我们编写的引导扇区工作啦

​ 完成一些初始化工作

​ 设置GDT , IDT

​ 关中断等一系列必要的操作

以上的过程称为处理器的实模式运行

最后, CPU 执行指令寄存器,会跳转到操作系统入口代码处, 执行操作系统的控制过程

Bootstraping 实际上就是一个BootLoaders, 用来状态操作系统的小程序


Grub : 广为使用的引导程序,

  • multiboot spec , 只要满足multiboot标准既可以执行

    https://www.gun.org/software/grub/

  • Linux Loader

  • Syslinux

linux0.1 是没有提供bootloader的

文件准备: # bootsect.s Makefile , gitignore , ld.script

如何编写一个bootsect

启动引导程序(汇编代码) : bootsect.s

cat > bootsect.s <<"EOF"

########################################
#       实验一:                         #
#        编写一个启动引导bootsect.s       #
#                                      #
########################################

        .code16   # 编写的是16进制实模式的bootsect

        .global  _bootstart    # 导出符合_bootstart 供连接器使用, 
       #只有在global声明导出的符号,才会在将其汇编成一个object的时候,可以被其他的文件引用, 否则其他的文件不可见

        .equ BOOTSEG, 0x07c0    #启动扇区的段号是0x7c00, BIOS 会跳转到0x7c00的地址对bootsect代码进行执行
# BOIS 在读取bootect启动的引导的时候,是默认读取地址为0x7c00的地址处的代码 那么这里为什么写 0x07c0呢?  
# 这是因为,8086的地址线的物理结构:20根, 也就是他可以访问的物理寻址范围为2^20 即1M空间;
# 由于8086/8088所使用的寄存器都是16位,能够表示的地址范围为0~64k, 这和1M 地址空间比较也太小了, 
#  所以为了能在8086/8088下能够访问1M内存, Intel采取了分段模式: 对地址转换, 一个地址是由 16位段基地址 : 16位偏移构成的
# 例如:  段(seg)地址 0x07c0  偏移量(offset)为: 0x0000   即为  0x07c0:0x0000
#   通过地址转换, 他实际的物理地址是  将16位段基地址左移4位 + 16位偏移量 = 20位地址  == 0x7c00
#

# ljmp 有两个目的, 
#       第一个目的: 更换掉cs:ip两个寄存器,将代码段寄存器指向0x07c0 这样一个地址,将指令指针指向代码的入口地址
#       第二个目的: 清理掉之前的缓存,和流水线缓存, 保证cpu执行完BIOS到我们的bootsect代码执行过程中,不会出现错误

ljmp  $BOOTSEG, $_bootstart  

# 定义一个死循环, 用于启动引导成功后, 暂停
_bootstart:
        jmp  _bootstart


#  .=510 表示从以上生成的机器码结尾开始,以0补充到510个字节;
.= 510

#  BIOS 如何确定某个扇区可以启动? 
#       就是通过检查每一介质的第一扇区的最后两个字节是否为低字节55 高字节aa, 如果是表明是可以启动的扇区
#       一旦找到了这个扇区, 就会将这个扇区的指令装载到0x7c00处
signature:
        .word  0xaa55   
        #bootsect 的签名signature  
        #.word 定义一个world 为 aa55  , 指明当前扇区为启动引导扇区
        #为什么是aa55呢? 因为Intel 的架构是小端序, 前面的内容在地址的高位, 后面的内容在地址的低位存放


EOF

测试编译一下,检查编写是否有问题,如果目录下成功生成以个test.o, 表明没有问题

as  --32  bootsect.s  -o  test.o

删除临时文件test.o

rm  test.o
编写Makefile

对bootsect.s 文件进行构建

cat> Makefile<<"EOF"
############################################
# 说明: @号标识指令只执行,但指令本身不会输出到屏幕  
############################################
.PHONY=clean  run-qemu # 表示这是一个伪target

# 指定一个target: run-qemu 并且依赖于bootsect.o文件, target: 后面跟依赖文件列表(以空格分隔) 
run-qemu: bootsect.o
        @qemu-system-i386 --boot  a  -fda  bootsect.o
        # qemu-i386 指定qemu运行的架构启动, 
        # --boot  a  指定从软盘a盘启动
        # -fda  bootsect   将 bootsect.o文件加载到软盘中


# 定义一个target为bootsect.o 所谓target 就相当于为该文件的执行后可添加的option选项或者方法名  
bootsect.o:
        @as  --32   bootsect.s  -o  bootsect.o
        #  编译指令
        # --32 指定 以16位i8086平台进行编译为二进制
        # -o 指定输出二进制文件的文件名


# 定义一个清理临时文件的 target
clean:
        @rm -f  *.o 
        # 清理文件的临时输出二进制文件 *.o  清理所有以.o结尾的文件
EOF


小贴士:

直接运行makw bootsect.o 报错如下:

Makefile: missing separator(did you mean TAB instead of 8 spaces?). Stop.

由于Makefile文件的指令入口是Tab, 而 上面创建的Makefile的target指令是以8个空格起始,

要修改Makefile 中的8个空格为Tab空格, 注意这里的8需要根据vim的tab空格设置而定

sed 替换方式:
sed -i "s/        /\t/g"  Makefile
make测试:
make  bootsect.o

当前路径下会生成一个bootsect.o的可执行文件

查看其反汇编代码:

objdump  -S   bootsect.o 

查看他的汇编信息如下
在这里插入图片描述

发现和我们编写的汇编代码bootsect.s 不一致, 这是因为没有指定正确的架构,因为我们编译时 使用 --32 指明了他输出的是一个16位i8086平台的二进制文件

因此我们使用参数 -m i8086 进行反汇编代码查看如下
在这里插入图片描述
可以看到, 代码与我们你编写的一致了

再次测试

make  bootsect.o

查兰当前目录下生成了bootsect.o文件

测试qemu 启动 ,注意 qemu 工具需要安装在linux 环境下, 且qemu需要在图形化界面中启动:

make  run-qemu

该命令将bootsect.o加载到软盘a中,并使用qemu虚拟化工具从盘启动引导程序

出现如下错误:
在这里插入图片描述

提示找不到启动引导盘

为什么不能直接装载软盘a中bootsect.o文件, 我们命名将文件结尾附加了aa55的标识符?

我们查看bootsect.o的汇编:

objdump  -S  bootsect.o

在这里插入图片描述
可见, bootsect的汇编代码时从ea 00 00 开始

使用16进制方式查看bootsect.o文件;

hexdump  -C  bootsect.o

在这里插入图片描述

首先文件的大小已经超出我们需要的512字节的大小, 且bootsect.o的代码被嵌入在可执行文件的中间

实际上 ea 00 00 之前的部分是ELF文件的head部分, 系统根据ELF 的head 判断是可执行文件

但是我们的BIOS是运行在实模式下, 不需要ELF head部分的信息, 他是直接跳转到段地址0x0000起始地址直接加载bootsect编译的代码 到内存的0x7c000处, 执行代码

因此需要从bootsec.o文件中提取出有效的执行代码段

关于可执行文件的段信息:

查看段信息:

objdump  -s   bootsect.o

在这里插入图片描述
编译生成可执行文件bootsect.o 仅有一个段.text 信息

使用命令

objcopy  -O  binary  -j  .text   bootsect.o
#  objcopy  去掉 bootsect 文件的ELF的文件头信息
# -O  binary  指定输出的格式为binary格式
#  -j 指定提取bootsect二进制文件中的 .text段的信息 , 由于这里仅有一个.text. 此项可以省略

在这里插入图片描述
发现第一个字节是ea , 最后也是一个启动引导表示55aa

至此, 一个真正的实模式下,可以在BIOS中启动引导的可执行文件准备完成

使用 qemu 加载启动引导项运行
在这里插入图片描述
在Makefile 中 添加文件提取操作, 该文件中使用了自定义连接规则ld-bootsect.ld来过滤段地址提取仅需的 .text段信息, 效果和通过命令行方式objectcopy是一样的;但是这种方式体现了模块化优势,为后续的开发提供更好的扩展

编写连接规则文件如下:

cat  > ld-bootsect.ld <<EOF

OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386)
SECTIONS{
        .text  0x0000 : {
                *(.text)
                /* 指定 提取text段 , 且指定段的起始地址为 0x0000  */
        }

        /DISCARD/ : {
                /* 这是一个默认丢弃段的规则, 写在这里面的段都会被丢弃掉   */
        }

}

EOF
    
sed -i "s/        /\t/g"  Makefile
cat> Makefile<<"EOF"
############################################
# 说明: @号标识指令只执行,但指令本身不会输出到屏幕  
############################################
.PHONY=clean  run-qemu # 表示这是一个伪target

# 指定一个target: run-qemu 并且依赖于bootsect文件, target: 后面跟依赖文件列表(以空格分隔) 
run-qemu: bootsect.o
        @qemu-system-i386 --boot  a  -fda  bootsect.o
        # qemu-i386 指定qemu运行的架构启动, 
        # --boot  a  指定从软盘a盘启动
        # -fda  bootsect   将 bootsect.o文件加载到软盘中


# 定义一个target为bootsect.o 所谓target 就相当于为该文件的执行添加的option选项或者方法名  
bootsect.o:
        @as  --32   bootsect.s  -o  bootsect.o
        #  编译指令
        # --32 指定 以16位i8086平台进行编译为二进制
        # -o 指定输出二进制文件的文件名

# 提取有效段执行代码 ,依赖文件 bootsect.o 和 ld-bootsect.ld 文件
bootsect:  bootsect.o  ld-bootsect.ld
        #  
        # link-script 是将不同的object 文件以规定的规则组合在一起, 并对他们重新进行排列
        # 每个二进制文件都是由不同的段组成的
        #
        # 可以使用 objdump -S 可执行二进制文件名, 就可以查看该二进制文件的段信息. 
        # 例如 .text 段, .data 段
        # gcc 在编译连接时, 默认的连接中使用的规则我们是不能使用的
        # 因为我们bootsect仅仅需要.txt段 并且该.text的段起始地址为0000
        
        # 因此需要自定ln规则文件 ld-bootsect.ld
        @- ld  -T  ld-bootsect.ld  bootsect.o  -o  bootsect
        #  -T  指定我们自己的link-script 连接规则文件
        #  bootsect.o作为输入文件  
        # -o 输出文件为 bootsect 
        
        @- objcopy  -j  .text  -O  binary    bootsect
		#  objcopy  用来指定规则 提取有效信息,去掉 bootsect 文件的ELF的文件头信息
		# -O  binary  指定输出的格式为binary格式
		#  -j 指定提取bootsect二进制文件中的 .text段的信息



# 定义一个清理临时文件的 target
clean:
        @rm -f  *.o 
        # 清理文件的临时输出二进制文件 *.o  清理所有以.o结尾的文件
EOF


sed -i "s/        /\t/g"  Makefile

测试 :

make run-qemu

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值