1 Star 0 Fork 1

louislin19/course-xv6

forked from 殊蕤/xv6-course 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
13-xv6-startup.org 7.94 KB
一键复制 编辑 原始数据 按行查看 历史
殊蕤 提交于 2023-11-06 07:23 . add

xv6 启动流程分析

xv6.img 构建过程

首先 xv6-public 是通过 make 编译系统构建,构建流程如下

make -nB | sed 's/gcc.*-c/gcc -c/'
gcc -c bootmain.c
gcc -c bootasm.S
ld -m    elf_i386 -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
objdump -S bootblock.o > bootblock.asm
objcopy -S -O binary -j .text bootblock.o bootblock
./sign.pl bootblock
gcc -c -o bio.o bio.c
gcc -c -o console.o console.c
gcc -c -o exec.o exec.c
gcc -c -o file.o file.c
gcc -c -o fs.o fs.c
gcc -c -o ide.o ide.c
gcc -c -o ioapic.o ioapic.c
gcc -c -o kalloc.o kalloc.c
gcc -c -o kbd.o kbd.c
gcc -c -o lapic.o lapic.c
gcc -c -o log.o log.c
gcc -c -o main.o main.c
gcc -c -o mp.o mp.c
gcc -c -o picirq.o picirq.c
gcc -c -o pipe.o pipe.c
gcc -c -o proc.o proc.c
gcc -c -o sleeplock.o sleeplock.c
gcc -c -o spinlock.o spinlock.c
gcc -c -o string.o string.c
gcc -c -o swtch.o swtch.S
gcc -c -o syscall.o syscall.c
gcc -c -o sysfile.o sysfile.c
gcc -c -o sysproc.o sysproc.c
gcc -c -o trapasm.o trapasm.S
gcc -c -o trap.o trap.c
gcc -c -o uart.o uart.c
./vectors.pl > vectors.S
gcc -c -o vectors.o vectors.S
gcc -c -o vm.o vm.c
gcc -c -o entry.o entry.S
gcc -c entryother.S
ld -m    elf_i386 -N -e start -Ttext 0x7000 -o bootblockother.o entryother.o
objcopy -S -O binary -j .text bootblockother.o entryother
objdump -S bootblockother.o > entryother.asm
gcc -c initcode.S
ld -m    elf_i386 -N -e start -Ttext 0 -o initcode.out initcode.o
objcopy -S -O binary initcode.out initcode
objdump -S initcode.o > initcode.asm
ld -m    elf_i386 -T kernel.ld -o kernel entry.o bio.o console.o exec.o file.o fs.o ide.o ioapic.o kalloc.o kbd.o lapic.o log.o main.o mp.o picirq.o pipe.o proc.o sleeplock.o spinlock.o string.o swtch.o syscall.o sysfile.o sysproc.o trapasm.o trap.o uart.o vectors.o vm.o  -b binary initcode entryother
objdump -S kernel > kernel.asm
objdump -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel.sym
dd if=/dev/zero of=xv6.img count=10000
dd if=bootblock of=xv6.img conv=notrunc
dd if=kernel of=xv6.img seek=1 conv=notrunc

启动编译阶段:

  1. bootasm.S, bootmain.c 构建 bootblock
    • 产出汇编文件 bootblock.asm
    • 产出 MBR 文件 bootblock
  2. 编译入口文件, entry.S, entryother.S, initcode.S
    • entry.o entry 函数, 启动入口函数 (paging off)
    • entryother
    • initcode start 函数
  3. 编译内核文件, bio.c, console.c … swtch.S, trap.c
    • c 文件和汇编单个编译,混合链接
    • 产出 kernel.asm
    • 产出 kernel 内核镜像文件
    • kernel 起始地址 0x80100000, 见 kernel.ld
  4. 合成启动镜像 bootblock, kernel
    • 产出 xv6.img
    • xv6.img 512 bootblock
    • xv6.img >512 kernel
      • 4096

bootloader 阶段

系统启动文件 ../../study/os/xv6-public/bootasm.S

读取磁盘函数 readsect ../../study/os/xv6-public/bootmain.c

磁盘读取发展 IO 操作读取硬盘的三种方式:

  1. CHS 方式 :小于 8G (8064MB)
  2. LBA28 方式:小于 137GB
  3. LBA48 方式:小于 144,000,000 GB

LBA 方式访问使用了 data 寄存器,LBA 寄存器(总共 3 个),device 寄存器,command 寄存器来完成的。

LBA28 和 LBA48 方式:LBA28 方式使用 28 位来描述一个扇区地址,最大支持 128GB 的硬 磁盘容量。

xv6 使用 LBA28 方式读取硬盘中的数据

  1. 0x1F0 data 寄存器
    • 已经读取或写入的数据,大小为两个字节(16 位数据) 每次读取 1 个 word
    • 反复循环,直到读完所有数据
  2. 0x1F1 features 寄存器
    • 读取时的错误信息 写入时的额外参数
  3. 0x1F2 sector count 寄存器
    • 指定读取或写入的扇区数
  4. 0x1F3 LBA low 寄存器
    • lba 地址的低 8 位
  5. 0x1F4 LBA mid 寄存器
    • lba 地址的中 8 位
  6. 0x1F5 LBA high 寄存器
    • lba 地址的高 8 位
  7. 0x1F6 device 寄存器
    • lba 地址的前 4 位(占用 device 寄存器的低 4 位)
    • 主盘值为 0(占用 device 寄存器的第 5 位)
    • 第 6 位值为 1 LBA 模式为 1,
    • CHS 模式为 0(占用 device 寄存器的第 7 位) 第 8 位值为 1
  8. 0x1F7 command 寄存器
    • 读取,写入的命令,返回磁盘状态 1
    • 读取扇区:0x20 写入扇区:0x30 磁盘识别:0xEC
  • grub2
  • uboot

内联汇编形式

asm [volatile] (“Instruction list”:Output:Input:Clobber/Modify)

从 port 端口读取一个 byte,返回这个读取的值。 使用内联汇编,指定把 data 放在%al 存储,port 放在%dx 存储,然后调用 inb %dx, %al

static inline uchar
inb(ushort port)
{
  uchar data;

  asm volatile("in %1,%0" : "=a" (data) : "d" (port)); // =a, al 寄存器, d 表示 dx 寄存器
  return data;
}

读取端口的字节到给定地址

static inline void
insl(int port, void *addr, int cnt)  // 从端口 port 读 4*cnt 个字节到地址 addr
{
  asm volatile("cld; rep insl" :                    // 清零 DF 位,重复指令 insl
               "=D" (addr), "=c" (cnt) :            // addr 目的地址绑定寄存器 edi,cnt 循环次数绑定 ecx
               "d" (port), "0" (addr), "1" (cnt) :  // port 端口绑定 dx,addr,cnt 同上
               "memory", "cc");                     // 改变了内存,改变了eflags寄存器
}

重复执行 insl 指令来读取多个字节的数据到目的地址,

  • 具体操作顺序是先读目的地址和循环次数,insl 每次执行后再更新 addr,cnt 的值,所 以 addr 和 cnt 是又读又写的
  • Output 和 Input 部分都存在 addr 和 cnt。期间改变了内存和 eflags 寄存器,所以 内联汇编的最后一部分申明 “cc” 和 “memory”

kernel 执行阶段

  1. entry.S 配置起始地址 0x10000c
  2. 进行初始化操作(先忽略)
  3. 跳转到 main 方法

查看 ELF 头

readelf -h kernel
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x10000c
  Start of program headers:          52 (bytes into file)
  Start of section headers:          134731012 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         3
  Size of section headers:           40 (bytes)
  Number of section headers:         16
  Section header string table index: 15

参考程序段

readelf -l kernel

Elf file type is EXEC (Executable file)
Entry point 0x10000c
There are 3 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x80100000 0x00100000 0x07b6b 0x07b6b R E 0x1000
  LOAD           0x009000 0x80108000 0x00108000 0x804a5c6 0x8055590 RW  0x1000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10

 Section to Segment mapping:
  Segment Sections...
   00     .text .rodata
   01     .data .bss
   02

总结

  1. bootasm.S -> bootmain.c:bootmain()
  2. bootmain
    • 加载 kernel 到内存
    • 解析 elf 头
    • 跳转 elf->entry()
  3. entry.S
    • 初始化
    • 跳转 main.c:main()
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/louislin19/course-xv6.git
git@gitee.com:louislin19/course-xv6.git
louislin19
course-xv6
course-xv6
master

搜索帮助