自己动手搭建 Linux 0.12 编译环境 — Boot
2013-04-20 11:18
603 查看
通过前面两篇的介绍,相信对搭建Linux 0.12编译环境的诸多工具有了基础的了解,但不论是Bochs还是Linux主机,它们只是你搭建过程中的棋子而已,因为真正的工作环境是Linux 0.12 OS,前面铺垫了很多就是为了它能快速简单的搭建。
下面两篇的内容个人觉得很赞,是搭建Linux 0.12 OS的重点。首先通过这篇文章我们来了解一下Linux 0.12的启动过程,然后学习如何修改bootsect.s使其从硬盘启动。这是一个非常酷的定制工作,我们按照自己的想法来实现它,脱离原来的束缚且从中可以学到更多知识。
[硬盘规划].
硬盘就是我们所说的启动盘,它包括了boot扇区、内核映像(setup & system)、文件系统以及swap分区。还记得Linux
0.11吗,启动它需要两个软盘文件,一个包含了boot扇区和内核映像,一个包含了文件系统,如果是Linux 0.12只需在***一个swap设备即可。硬盘唯一的好处就是将它们都整合在了一起。
这样就需要对硬盘进行相应的规划,基本雷打不动的就是boot扇区,它在开机的时候需要被BIOS加载,因此硬盘的第一扇区就是boot扇区。至于文件系统和swap分区,它们分别作为两个分区。内核映像非常关键,因为boot要找到它然后把它加载到内存中运行,所以我们采用了一种布局,直接将它放在boot扇区之后,即第一分区里面。后面你就会发现这样做可以减轻改写boot代码的难度。
[启动过程].
我们来回顾一下Linux 0.12的启动过程,我把关键的部分拿出来:
1. BIOS把启动盘的第一个扇区(bootsect)拷贝到物理地址0x7C00处
2. bootsect拷贝自己到物理地址0x90000处
3. bootsect拷贝内核映像setup(4个扇区)到物理地址0x90200处
4. bootsect拷贝内核映像system(0x3000*16 bytes = 192K)到物理地址0x10000处
后面的步骤不多述了,和这篇的主题无关。对于boot到这儿就差不多了,完整的描述了bootsect.S的功能。不要忘记了我们说启动过程的目的,在回想一下,Linux 0.11用一张软盘来存放了boot扇区和内核映像,其实心细的你已经发现上面提到的硬盘里面的boot扇区和内核映像布局其实保持了一致性,因此这里你就可以清楚明白这样做的目的完全是为了减轻改写boot代码的难度。既然要改写,说明软盘和硬盘的读取参数(BIOS interrupt 13h)上有些不同,主要是因为软盘和硬盘的结构不同,软盘(通常指1.44M软盘)每个磁道的扇区数为18,磁头数为2,而硬盘每个磁道的扇区数为63,磁头数为16,因此我们需要对bootsect.S做个小手术。
[硬盘boot].
这里我已经将bootsect.S用ATT汇编进行了重写,在此基础上进行修改使其能从硬盘中boot起来。
下面两篇的内容个人觉得很赞,是搭建Linux 0.12 OS的重点。首先通过这篇文章我们来了解一下Linux 0.12的启动过程,然后学习如何修改bootsect.s使其从硬盘启动。这是一个非常酷的定制工作,我们按照自己的想法来实现它,脱离原来的束缚且从中可以学到更多知识。
[硬盘规划].
硬盘就是我们所说的启动盘,它包括了boot扇区、内核映像(setup & system)、文件系统以及swap分区。还记得Linux
0.11吗,启动它需要两个软盘文件,一个包含了boot扇区和内核映像,一个包含了文件系统,如果是Linux 0.12只需在***一个swap设备即可。硬盘唯一的好处就是将它们都整合在了一起。
这样就需要对硬盘进行相应的规划,基本雷打不动的就是boot扇区,它在开机的时候需要被BIOS加载,因此硬盘的第一扇区就是boot扇区。至于文件系统和swap分区,它们分别作为两个分区。内核映像非常关键,因为boot要找到它然后把它加载到内存中运行,所以我们采用了一种布局,直接将它放在boot扇区之后,即第一分区里面。后面你就会发现这样做可以减轻改写boot代码的难度。
[启动过程].
我们来回顾一下Linux 0.12的启动过程,我把关键的部分拿出来:
1. BIOS把启动盘的第一个扇区(bootsect)拷贝到物理地址0x7C00处
2. bootsect拷贝自己到物理地址0x90000处
3. bootsect拷贝内核映像setup(4个扇区)到物理地址0x90200处
4. bootsect拷贝内核映像system(0x3000*16 bytes = 192K)到物理地址0x10000处
后面的步骤不多述了,和这篇的主题无关。对于boot到这儿就差不多了,完整的描述了bootsect.S的功能。不要忘记了我们说启动过程的目的,在回想一下,Linux 0.11用一张软盘来存放了boot扇区和内核映像,其实心细的你已经发现上面提到的硬盘里面的boot扇区和内核映像布局其实保持了一致性,因此这里你就可以清楚明白这样做的目的完全是为了减轻改写boot代码的难度。既然要改写,说明软盘和硬盘的读取参数(BIOS interrupt 13h)上有些不同,主要是因为软盘和硬盘的结构不同,软盘(通常指1.44M软盘)每个磁道的扇区数为18,磁头数为2,而硬盘每个磁道的扇区数为63,磁头数为16,因此我们需要对bootsect.S做个小手术。
[硬盘boot].
这里我已经将bootsect.S用ATT汇编进行了重写,在此基础上进行修改使其能从硬盘中boot起来。
# # SYS_SIZE is the number of clicks (16 bytes) to be loaded. # 0x3000 is 0x30000 bytes = 196kB, more than enough for current # versions of linux # #include <linux/config.h> SYSSIZE = DEF_SYSSIZE # # bootsect.s (C) 1991 Linus Torvalds # modified by Drew Eckhardt # # bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves # iself out of the way to address 0x90000, and jumps there. # # It then loads 'setup' directly after itself (0x90200), and the system # at 0x10000, using BIOS interrupts. # # NOTE! currently system is at most 8*65536 bytes long. This should be no # problem, even in the future. I want to keep it simple. This 512 kB # kernel size should be enough, especially as this doesn't contain the # buffer cache as in minix # # The loader has been made as simple as possible, and continuos # read errors will result in a unbreakable loop. Reboot by hand. It # loads pretty fast by getting whole sectors at a time whenever possible. .code16 .globl begtext, begdata, begbss, endtext, enddata, endbss .text begtext: .data begdata: .bss begbss: .text SETUPLEN = 4 # nr of setup-sectors BOOTSEG = 0x07c0 # original address of boot-sector INITSEG = DEF_INITSEG # we move boot here - out of the way SETUPSEG = DEF_SETUPSEG # setup starts here SYSSEG = DEF_SYSSEG # system loaded at 0x10000 (65536). ENDSEG = SYSSEG + SYSSIZE # where to stop loading # ROOT_DEV & SWAP_DEV are now written by "build". ROOT_DEV = 0 SWAP_DEV = 0 .globl boot_start boot_start: /* * BIOS会把一下代码拷贝到物理内存地址0x7C00处 */ mov $BOOTSEG,%ax mov %ax,%ds /* * 拷贝自己到物理内存地址0x90000处 */ mov $INITSEG,%ax mov %ax,%es mov $256,%cx sub %si,%si sub %di,%di rep movsw ljmp $INITSEG,$go go: mov %cs,%ax mov $0xfef4,%dx # arbitrary value >>512 - disk parm size mov %ax,%ds mov %ax,%es push %ax mov %ax,%ss # put stack at 0x9ff00 - 12. mov %dx,%sp /* * Many BIOS's default disk parameter tables will not * recognize multi-sector reads beyond the maximum sector number * specified in the default diskette parameter tables - this may * mean 7 sectors in some cases. * * Since single sector reads are slow and out of the question, * we must take care of this by creating new parameter tables * (for the first disk) in RAM. We will set the maximum sector * count to 18 - the most we will encounter on an HD 1.44. * * High doesn't hurt. Low does. * * Segments are as follows: ds=es=ss=cs - INITSEG, * fs = 0, gs = parameter table segment */ push $0 pop %fs mov $0x78,%bx # fs:bx is parameter table address # seg fs push %ds lds %fs:(%bx),%si # ds:si is source mov %dx,%di # es:di is destination mov $6,%cx # copy 12 bytes cld rep movsw pop %ds mov %dx,%di movb $18,4(%di) # patch sector count # seg fs mov %di,%fs:(%bx) # seg fs mov %es,%fs:2(%bx) pop %ax mov %ax,%fs mov %ax,%gs xor %ah,%ah # reset FDC xor %dl,%dl int $0x13 # load the setup-sectors directly after the bootblock. # Note that 'es' is already set up. /* * 拷贝内核映像setup(4个扇区)到物理内存地址0x90200处 * * int 13h - driver test PS/2 * ah = 00h - reset disk drive * ah = 01h - get status of last drive operation * ah = 02h - read sectors from drive * ah = 03h - write sectors to drive * ah = 04h - verify sectors from drive * ah = 05h - format track * ah = 06h - format track set bad sector flags * ah = 07h - format drive starting at track * ah = 08h - read drive parameters * ... * parameters: * al : sectors of read count * cl[7:6]+ch[7:0] : track, [0,] * cl[5:0] : sector, [1,] * dh : head, [0,] * dl : drive, (floppy A =00h, harddisk =80h) * es:bx : buffer address pointer * * result: * cf : set on error, clear if no error * ah : return code * al : actual sectors read count */ load_setup: mov $0x0080,%dx # drive 0, head 0 mov $0x0002,%cx # sector 2, track 0 mov $0x0200,%bx # address = 512, in INITSEG mov $(0x0200+SETUPLEN),%ax # service 2, nr of sectors int $0x13 # read it jnc ok_load_setup # ok - continue # push %ax # dump error code # call print_nl # mov %sp,%bp # call print_hex # pop %ax xor %dl,%dl # reset FDC xor %ah,%ah int $0x13 jmp load_setup ok_load_setup: # Get disk drive parameters, specifically nr of sectors/track /* * 获取磁盘参数 * track_tl : 柱面数 * head_tl : 磁头数 * sectors : 扇区数 * * parameters: * dl : drive index * * result: * cf : set on error, clear if no error * ah : return code * dl : number of hard disk drives * dh : logical last index of heads * cx : [7:6][15:8] logical last index of cylinders, * [5:0] logical last index of sectors per track * bl : drive type (only AT/PS2 floppies) */ mov $0x80,%dl mov $0x0800,%ax # AH=8 is get drive parameters int $0x13 mov %cl,%al shr $6,%al mov %al,%ah mov %ch,%al movw %ax,track_tl mov %dh,%dl xor %dh,%dh movw %dx,head_tl xor %ch,%ch and $0x3f,%cl # cl(6~7)=0 movw %cx,sectors mov $INITSEG,%ax mov %ax,%es # Print some inane message mov $0x03,%ah # read cursor pos xor %bh,%bh int $0x10 mov $24,%cx mov $0x0007,%bx # page 0, attribute 7 (normal) mov $msg1,%bp mov $0x1301,%ax # write string, move cursor int $0x10 # ok, we've written the message, now # we want to load the system (at 0x10000) mov $SYSSEG,%ax mov %ax,%es # segment of 0x010000 call read_it # call kill_motor # call print_nl # After that we check which root-device to use. If the device is # defined (!= 0), nothing is done and the given device is used. # Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending # on the number of sectors that the BIOS reports currently. # seg cs mov root_dev,%ax or %ax,%ax jne root_defined # seg cs mov sectors,%bx mov $0x0208,%ax # /dev/ps0 - 1.2Mb cmp $15,%bx je root_defined mov $0x021c,%ax # /dev/PS0 - 1.44Mb cmp $18,%bx je root_defined undef_root: jmp undef_root root_defined: # seg cs mov %ax,root_dev # after that (everyting loaded), we jump to # the setup-routine loaded directly after # the bootblock: ljmp $SETUPSEG,$0 # This routine loads the system at address 0x10000, making sure # no 64kB boundaries are crossed. We try to load it as fast as # possible, loading whole tracks whenever we can. # # in: es - starting address segment (normally 0x1000) # sread: .word 1+SETUPLEN # sectors read of current track [0,] head: .word 0 # current head [0,] track: .word 0 # current track [0,] /* * 拷贝内核映像system(0x3000*16 bytes = 192K)到物理地址0x10000处 */ read_it: mov %es,%ax test $0x0fff,%ax die: jne die # es must be at 64kB boundary xor %bx,%bx # bx is starting address within segment /* * 判断是否将内核映像system拷贝完成 */ rp_read: mov %es,%ax cmp $ENDSEG,%ax # have we loaded all yet? jb ok1_read /* 若ax >= ENDSEG,则拷贝完成 */ ret /* * 把一个磁道的扇区读完否则把64K的段地址读满 */ ok1_read: mov sectors,%ax sub sread,%ax /* 把一个磁道的剩余扇区全读了 */ mov %ax,%cx shl $9,%cx add %bx,%cx /* 这里有暗示,进位只可能为1,因为把一个磁道的扇区全部 读完也只有63*512 = 7e00h字节 < 10000h 64K段 */ jnc ok2_read /* CF为0,说明64K段还有剩余 */ je ok2_read /* CF为1,ZF为1, 说明刚好把64K段读满了 */ xor %ax,%ax /* 读取的扇区数超过了64K段 */ sub %bx,%ax shr $9,%ax /* 把64K段读满,需要读的扇区数 */ /* * 读完一次磁盘后调整下次需要读取磁盘的位置(sread, head, track) */ ok2_read: call read_track mov %ax,%cx add sread,%ax cmp sectors,%ax jne ok3_read /* ZF为0,说明一个磁道的扇区都还没读完,可以不做调整 */ incw head mov head,%ax cmpw head_tl,%ax jne ok4_read /* ZF为0,说明一个柱面的磁头都还没读完 */ incw track /* 否则,需要调整到一下一个柱面上 */ xor %ax,%ax /* * 调整后的磁头号和初始化起始扇区为0 */ ok4_read: movw %ax,head xor %ax,%ax /* * 调整下次需要拷贝到的物理内存位置(es:bx) */ ok3_read: movw %ax,sread shl $9,%cx /* cx是之前保存的当前读取的扇区数 */ add %cx,%bx jnc rp_read /* CF为0,说明64K段还没读满,可以不做调整 */ mov %es,%ax add $0x1000,%ax /* CF为1,需要将es段加1个64K段 */ mov %ax,%es xor %bx,%bx /* 一个64K段刚好读完了,下一个段开始了 */ jmp rp_read /* * 读取磁盘数据 * * parameters: * ax : sectors of read count * sread : current sector [0,] * head : current head [0,] * track : current track [0,] * es:bx : buffer address pointer * * result: * * parameters: * al : sectors of read count * cl[7:6]+ch[7:0] : track, [0,] * cl[5:0] : sector, [1,] * dh : head, [0,] * dl : drive, (floppy A =00h, harddisk =80h) * es:bx : buffer address pointer */ read_track: push %ax push %bx push %cx push %dx mov track,%dx mov sread,%cx inc %cx mov %dl,%ch shl $6,%dh or %dh,%cl mov head,%dx mov %dl,%dh mov $0x80,%dl mov $0x02,%ah int $0x13 jc bad_rt pop %dx pop %cx pop %bx pop %ax ret bad_rt: mov $0,%ax mov $0x80,%dx int $0x13 pop %dx pop %cx pop %bx pop %ax jmp read_track /* * 下面print函数需要去掉的原因是boot扇区512B空间不够了 */ /* * print_all is for debugging purposes. * It will print out all of the registers. The assumption is that this is * called from a routine, with a stack frame like * dx * cx * bx * ax * error * ret <- sp * */ # because of size > 512 byte, so need decrease these /* print_all: mov $5,%cx # error code + 4 registers mov %sp,%bp print_loop: push %cx # save count left call print_nl # nl for readability jae no_reg # see if register name is needed mov $0xe05 + 0x41 - 1,%ax sub %cl,%al int $0x10 mov $0x58,%al # X int $0x10 mov $0x3a,%al # : int $0x10 no_reg: add $2,%bp # next register call print_hex # print it pop %cx loop print_loop ret print_nl: mov $0xe0d,%ax # CR int $0x10 mov $0xa,%al # LF int $0x10 ret */ /* * print_hex is for debugging purposes, and prints the word * pointed to by ss:bp in hexadecmial. */ /* print_hex: mov $4,%cx # 4 hex digits mov (%bp),%dx # load word into dx print_digit: rol $4,%dx # rotate so that lowest 4 bits are used mov $0xe,%ah mov %dl,%al # mask off so we have only next nibble and $0xf,%al add $0x30,%al # convert to 0 based digit, '0' cmp $0x39,%al # check for overflow jbe good_digit add $0x41 - 0x30 - 0xa,%al # 'A' - '0' - 0xa good_digit: int $0x10 loop print_digit ret */ /* * This procedure turns off the floppy drive motor, so * that we enter the kernel in a known state, and * don't have to worry about it later. */ kill_motor: push %dx mov $0x3f6,%dx mov $0,%al outb %al,%dx pop %dx ret sectors: # total sect .word 0 head_tl: # total head .word 0 track_tl: # total track .word 0 msg1: .byte 13,10 .ascii "Loading system ..." .byte 13,10,13,10 /* * 定位这里的目的是从0x1bc开始是存放硬盘分区信息的位置 */ .org 0x1bc end_end: .byte 0 /* * 存放交换分区、文件系统分区设备号 */ .org 506 swap_dev: .word SWAP_DEV root_dev: .word ROOT_DEV boot_flag: .word 0xAA55 .text endtext: .data enddata: .bss endbss:
相关文章推荐
- 自己动手搭建 Linux 0.12 编译环境 — Makefile
- 自己动手搭建 Linux 0.12 编译环境 — 开篇
- 自己动手搭建 Linux 0.12 编译环境 — Bochs
- 自己动手搭建 Linux 0.12 编译环境 — 编译
- 自己动手搭建 Linux 0.12 编译环境 — Linux主机
- 在Ubuntu13.10上自己动手搭建交叉编译环境arm-linux-gcc-4.8.2
- 在Ubuntu13.10上自己动手搭建交叉编译环境arm-linux-gcc-4.8.2
- 自己动手搭建一个简易的SpringBoot环境
- Ubuntu(Linux)使用Eclipse搭建C/C++编译环境
- 自己制作arm-linux交叉编译环境(一)-scratch篇
- ubuntu9.04下搭建交错编译环境arm-linux-gcc
- 搭建arm-linux-gcc交叉编译环境
- Ubuntu(Linux)使用Eclipse搭建C/C++编译环境
- Mac OS X & Linux下搭建Nrf51822开发环境与编译过程
- 搭建dubbo+zookeeper环境(Linux环境、Windows可自己尝试)
- arm+linux 裸机环境搭建之初试minicom+dnw烧写uboot
- Linux|UNIX下LAMP环境的搭建及常见问题[连载9编译Apache]
- 自己动手在Red Hat Enterprise 6.0上搭建LAMP开发环境,你也可以的!
- 自己动手搭建 Redis 环境,并建立一个 .NET HelloWorld 程序测试
- 如何使用编译生成的release搭建nfs boot环境