码迷,mamicode.com
首页 > 其他好文 > 详细

ucore lab1 bootloader学习笔记

时间:2016-08-17 22:47:46      阅读:529      评论:0      收藏:0      [点我收藏+]

标签:

---恢复内容开始---

 

 开机流程回忆

 

以Intel 80386为例,计算机加电后,CPU从物理地址0xFFFFFFF0(由初始化的CS:EIP确定,此时CS和IP的值分别是0xF000和0xFFF0))开始执行。在0xFFFFFFF0这里只是存放了一条跳转指令,通过跳转指令跳到BIOS例行程序起始点。BIOS做完计算机硬件自检和初始化后,会选择一个启动设备(例如软盘、硬盘、光盘等),并且读取该设备的第一扇区(即主引导扇区或启动扇区)到内存一个特定的地址0x7c00处,然后CPU控制权会转移到那个地址继续执行。至此BIOS的初始化工作做完了,进一步的工作交给了ucore的bootloader。

简单来说,就是

加电-->CPU执行内存地址为0xFFFFFFF0的指令(一条跳转指令)--->跳到BIOS程序起始点,执行BIOS程序(功能:计算机硬件自检和初始化),自检等完成后--->选择一启动设备,并读取第一扇区(主引导扇区)到内存的0x7c00处--->BIOS所有工作完成后, CPU执行内存的0x7c00中的程序,即bootloader。

 

bootloader启动过程

 BIOS将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行bootloader。bootloader完成的工作包括:

  • 打开A20地址线,设置CR0,切换到保护模式,启用分段机制
  • 读磁盘中ELF执行文件格式的ucore操作系统到内存
  • 显示字符串信息
  • 把控制权交给ucore操作系统

 

对应其工作的实现文件在lab1中的boot目录下的三个文件asm.h、bootasm.S和bootmain.c。

 

 代码简单分析

bootasm.S

 

1 .code16                                             # Assemble for 16-bit mode
2     cli                                             # Disable interrupts
3     cld                                             # String operations increment
4 
5     # Set up the important data segment registers (DS, ES, SS).
6     xorw %ax, %ax                                   # Segment number zero
7     movw %ax, %ds                                   # -> Data Segment
8     movw %ax, %es                                   # -> Extra Segment
9     movw %ax, %ss                                   # -> Stack Segment

 

注释:.code16表示为16位的实模式,cli表示屏蔽系统中断 6-9为清空ds , es , ss 等段寄存器内容。

 

 

 1     # Enable A20:
 2     #  For backwards compatibility with the earliest PCs, physical
 3     #  address line 20 is tied low, so that addresses higher than
 4     #  1MB wrap around to zero by default. This code undoes this.
 5 seta20.1:
 6     inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).
 7     testb $0x2, %al
 8     jnz seta20.1
 9 
10     movb $0xd1, %al                                 # 0xd1 -> port 0x64
11     outb %al, $0x64                                 # 0xd1 means: write data to 8042s P2 port
12 
13 seta20.2:
14     inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).
15     testb $0x2, %al
16     jnz seta20.2
17 
18     movb $0xdf, %al                                 # 0xdf -> port 0x60
19     outb %al, $0x60                                 # 0xdf = 11011111, means set P2s A20 bit(the 1 bit) to 1

 

 

注释: 参考“百度文库 激活A20地址线详解

 

1    # Switch from real to protected mode, using a bootstrap GDT
2     # and segment translation that makes virtual addresses
3     # identical to physical addresses, so that the
4     # effective memory map does not change during the switch.
5     lgdt gdtdesc
6     movl %cr0, %eax
7     orl $CR0_PE_ON, %eax
8     movl %eax, %cr0

注释:通过将CR0寄存器第一位置1,开启保护模式。

 

技术分享
1 # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)
2     movl $0x0, %ebp
3     movl $start, %esp
4     call bootmain
View Code

注释:跳转到bootmain.c

 

  bootmain.c

 

bootloader让CPU进入保护模式后,下一步的工作就是从硬盘上加载并运行OS。考虑到实现的简单性,bootloader的访问硬盘都是LBA模式的PIO(Program IO)方式,即所有的IO操作是通过CPU访问硬盘的IO地址寄存器完成。

 

一般主板有2个IDE通道,每个通道可以接2个IDE硬盘。访问第一个硬盘的扇区可设置IO地址寄存器0x1f0-0x1f7实现的,具体参数见下表。一般第一个IDE通道通过访问IO地址0x1f0-0x1f7来实现,第二个IDE通道通过访问0x170-0x17f实现。每个通道的主从盘的选择通过第6个IO偏移地址寄存器来设置。

 

表一 磁盘IO地址和对应功能

第6位:为1=LBA模式;0 = CHS模式 第7位和第5位必须为1

IO地址 功能
0x1f0 读数据,当0x1f7不为忙状态时,可以读。
0x1f2 要读写的扇区数,每次读写前,你需要表明你要读写几个扇区。最小是1个扇区
0x1f3 如果是LBA模式,就是LBA参数的0-7位
0x1f4 如果是LBA模式,就是LBA参数的8-15位
0x1f5 如果是LBA模式,就是LBA参数的16-23位
0x1f6 第0~3位:如果是LBA模式就是24-27位 第4位:为0主盘;为1从盘
0x1f7 状态和命令寄存器。操作时先给命令,再读取,如果不是忙状态就从0x1f0端口读数据

 

 

 

 

 

 

 

 

 

当前 硬盘数据是储存到硬盘扇区中,一个扇区大小为512字节。读一个扇区的流程(可参看boot/bootmain.c中的readsect函数实现)大致如下:

 

  1. 等待磁盘准备好
  2. 发出读取扇区的命令
  3. 等待磁盘准备好
  4. 把磁盘扇区数据读到指定内存

 

 1 /* waitdisk - wait for disk ready */
 2 static void
 3 waitdisk(void) {
 4     while ((inb(0x1F7) & 0xC0) != 0x40)
 5         /* do nothing */;
 6 }
 7 
 8 /* readsect - read a single sector at @secno into @dst */
 9 static void
10 readsect(void *dst, uint32_t secno) {
11     // wait for disk to be ready
12     waitdisk();
13 
14     outb(0x1F2, 1);                         // count = 1
15     outb(0x1F3, secno & 0xFF);
16     outb(0x1F4, (secno >> 8) & 0xFF);
17     outb(0x1F5, (secno >> 16) & 0xFF);
18     outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);
19     outb(0x1F7, 0x20);                      // cmd 0x20 - read sectors
20 
21     // wait for disk to be ready
22     waitdisk();
23 
24     // read a sector
25     insl(0x1F0, dst, SECTSIZE / 4);
26 }

 

 

---恢复内容结束---

ucore lab1 bootloader学习笔记

标签:

原文地址:http://www.cnblogs.com/zslhg903/p/5781882.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!