操作系统内核Hack:(三)引导程序制作

操作系统内核Hack:(三)引导程序制作

关于本文涉及到的完整源码请参考MiniOS的v1_bootloader分支


1.制作方法

现在我们已经了解了关于BootLoader的一切知识,让我们开始动手做一个BootLoader吧!但真正开始之前,我们还要做出一个选择,在之前的讨论中我们曾说过,有两种学习和制作引导程序和操作系统内核的路线:1)《Orange’s:一个操作系统的实现》书中的路线;2)Linux 0.11的路线。

1.1 两种实现思路

具体来说,第一种路线就是将BootLoader和Kernel都放到预先格式化成FAT的软盘上,BootLoader通过FAT和ELF的知识定位并加载Kernel,借助FreeDOS启动或调试我们的操作系统。并非对FAT和DOS心存偏见,毕竟现在的确很多汇编语言书籍仍然以DOS作为学习平台,但我个人更喜欢第二种路线。在引导过程,我们不依赖任何文件系统,在裸扇区中加载运行第二阶段BootLoader和Kernel。进入Kernel之后,我们再挂载一个根文件系统。为何要这样做而不跟着《Orange’s》作者的思路走呢?因为我们毕竟主要学习和模仿的是*nix族的操作系统,既然如此,为何不纯粹一些?直接循着当年Linus在Linux 0.11中的思路,这才算是一次酣畅淋漓的操作系统内核的Hack之旅!

经过了本系列前两回对实验环境和底层编程基础知识的准备,加上刚才对实现思路的分析,现在我们就参考《Linux内核完全注释》中Linux 0.11的思路,借鉴《Orange’s:一个操作系统的实现》中NASM的汇编代码,“双剑合璧,威力无穷”。让我们站在前人的肩膀上,一起动手来实现一个属于我们自己的操作系统BootLoader!

1.2 Linux 0.11参考

既然要借鉴思路,我们就先看一下Linux 0.11这个成品是什么样子。因为《Linux内核完全注释》中提供的源代码链接要做很多手动修改和准备工作才能使用,而网上有很多热心的网友分享了开箱即用的0.11源码安装包。

1.2.1 编译内核

cdai@cdai ~/Source $ git clone https://github.com/yuanxinyu/Linux-0.11
cdai@cdai ~/Source $ cd Linux-0.11

cdai@cdai-Vostro-1450 ~/Source/Linux-0.11 $ make help
<<<<This is the basic help info of linux-0.11>>>

Usage:
     make --generate a kernel floppy Image with a fs on hda1
     make start -- start the kernel in qemu
     make debug -- debug the kernel in qemu & gdb at port 1234
     make disk  -- generate a kernel Image & copy it to floppy
     make cscope -- genereate the cscope index databases
     make tags -- generate the tag file
     make cg -- generate callgraph of the system architecture
     make clean -- clean the object files
     make distclean -- only keep the source code files

Note!:
     * You need to install the following basic tools:
          ubuntu|debian, qemu|bochs, ctags, cscope, calltree, graphviz 
          vim-full, build-essential, hex, dd, gcc 4.3.2...
     * Becarefull to change the compiling options, which will heavily
     influence the compiling procedure and running result.
     ...

<<<Be Happy To Play With It :-)>>>

cdai@cdai ~/Source/Linux-0.11 $ sudo apt-get install -y ctags cscope graphviz qemu
cdai@cdai ~/Source/Linux-0.11 $ make

1.2.2 下载硬盘IMG

从OldLinux下载硬盘镜像hdc-0.11.img,大小为127MB。将其拷贝到Linux0.11目录下后,就可以执行make start用qemu模拟Linux 0.11的运行环境了。

cdai@cdai ~/Source/Linux-0.11 $ tree

├── boot
├── fs
├── hdc-0.11.img
├── Image
├── include
├── init
├── kernel
├── lib
├── Makefile
├── Makefile.header
├── mm
├── README.md
├── readme.old
├── System.map
└── tools

8 directories, 10 files
cdai@cdai ~/Source/Linux-0.11 $ make start

1.2.3 安装运行

启动时,系统会从软盘引导,并挂载根文件系统:

SeaBIOS (version 1.7.4-20140219_122725-roseapple)

iPXE (http://ipxe.org) 00:0.3.0 C900 PCI2.10 PnP PMM+00FC1110+00F21110 C900

Booting from Floppy...

Loading system...

Partition table ok.
43012/62000 free blocks
19719/20666 free inodes
3446 buffers = 3528704 bytes buffer space
Free mem: 12574720 bytes
 Ok.
[/usr/root]# ls
README      hello       mtools.howto        shoelace.tar.Z
gcclib140   hello.c     shoe

2.文件目录组织

文件目录组织是按照打包后的模块划分,一级目录分为boot1、boot2、system三个文件夹,tools中放的则是打包工具类。对应《Orange’s》代码,boot1/bootsect.s相当于boot.asm,boot2/setup.s相当于loader.asm。

cdai@cdai ~/Workspace/syspace/1-assembly $ tree -d minios/
minios/
├── boot1
│   └── bootsect.s
├── boot2
│   └── setup.s
├── Makefile
├── system
│   ├── fs
│   ├── include
│   ├── init
│   ├── kernel
│   ├── lib
│   └── mm
└── tools
    └── build.c

10 directories, 4 files

3.实用工具

3.1 构建工具

3.1.1 Makefile

首先,Makefile份内的工作就是编译出bootsect、setup、system、build。之后,调用build处理前三者,最终产生拼接好的可引导Image镜像文件。在此过程中,Makefile还有一些小任务要完成。例如利用临时汇编文件tmp.s将system模块的大小拼接到bootsect.asm的头部,加载system的代码中会用到它。


#################
# Macro & Rule
#################

AS      = nasm
ASFLAGS = -f elf
CC      = gcc
CFLAGS  = -Wall -O
LD      = ld
# -Ttext org -e entry -s(omit all symbol info)
# -x(discard all local symbols) -M(print memory map)
LDFLAGS = -Ttext 0 -e main --oformat binary -s -x -M

%.o:    %.c
    $(CC) $(CFLAGS) -c -o $@ $<

#%.o:   %.asm
#   $(AS) $(ASFLAGS) -o $@ $<


#################
#   Default
#################

all:    Image


Image:  boot1/bootsect boot2/setup system/system tools/build    
    tools/build boot1/bootsect boot2/setup system/system > a.bin
    dd if=a.bin of=Image bs=8192 conv=notrunc
    rm -f a.bin

tools/build:    tools/build.c
    $(CC) $(CFLAGS) -o $@ $<

# SYSSIZE= number of clicks (16 bytes) to be loaded
boot1/bootsect: boot1/bootsect.asm system/system
    (echo -n "SYSSIZE equ (";ls -l system/system | grep system \
        | cut -d " " -f 5 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.asm
    cat $< >> tmp.asm
    $(AS) -o $@ tmp.asm
    rm -f tmp.asm

boot2/setup:    boot2/setup.asm
    $(AS) -o $@ $<

system/system:  system/init/main.o system/init/myprint.o
    $(LD) $(LDFLAGS) \
    system/init/main.o \
    system/init/myprint.o \
    -o $@ > System.map

system/init/main.o: system/init/main.c

system/init/myprint.o: system/init/myprint.asm
    $(AS) $(ASFLAGS) -o $@ $<


#################
# Create floppy
#################

disk:
    bximage -q -fd -size=1.44 Image

.PHONY: disk


#################
#   Start Vm
#################

start:  Image
    bochs -q -f bochsrc

qemu:   Image
    qemu-system-x86_64 -m 16M -boot a -fda Image

.PHONY: start


#################
#   GitHub
#################

commit:
    git add .
    git commit -m "$(MSG)"


#################
#   Clean
#################

clean:
    rm -f boot1/bootsect boot2/setup system/system tools/build 
    rm -f system/**/*.o
    rm -f a.bin tmp.asm System.map

.PHONY: clean

3.1.2 build.c

build.c主要完成一些不便于或无法在Makefile中实现的操作:

  1. 文件有效性的检查:例如bootsect末尾的0x55AA签名
  2. 文件填充:例如对不足四个扇区大小的setup进行填充
  3. 文件内容解析:例如解析出a.out或ELF格式的system,将其中二进制代码部分提取出来
  4. 拼接:将bootsect、setup、system三者拼接成Image可引导镜像
#include <stdio.h>      /* fprintf */
#include <string.h>
#include <stdlib.h>     /* exit */
#include <sys/types.h>  /* unistd.h needs this */
#include <unistd.h>     /* read/write */
#include <fcntl.h>


#define BUFFER_SIZE 1024

#define SETUP_SECTS 4   /* max number of sectors of setup */
#define STRINGIFY(x) #x /* cat string */

#define GCC_HEADER 1024 /* GCC header length */
#define SYS_SIZE 0x2000 /* max system length (SYS_SIZE*16=128KB) */


void die(const char *str)
{
    fprintf(stderr, "%s\n", str);
    exit(
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值