Lab1

 

lab1

启动顺序

1

相关寄存器设置初始值,cs寄存器初值为0xf000,ip寄存器初值为0xfff0,此时pc指针指向的内存地址为 base + offset = 0xfffffff0(0xffff0000 为cs寄存器隐含的值)1562054984591

此地址为bios的eprom地址,通常为长跳转指令,跳转至1m的bios代码中执行

(注:视频中地址长度为20位,两个16位寄存器相加,即cs左移4位+ip,地址为0xffff0)

2

bios加载存储设备第一个扇区的512字节到内存的0x7c00处,pc跳转至0x7c00处执行代码(此512字节代码称之为bootloader

(注:由于bios能力限制,不能一次加载完整个的os代码至内存中,故分多次加载)

bootloader的功能:

1.实模式切换到保护模式(从16位寻址空间切换至32位寻址空间)&段机制可以工作

2.读取kernel代码读入内存中固定区域内

3.将pc指针指向os的入口点,控制权交给操作系统

(注:cs等段寄存器在保护模式中(段机制)指向的是一个段描述符,段描述符中保存base等等信息)

保存段描述符的数组称之为全局描述符表(GDT)(段表),由bootloader建立,gdtr寄存器保存相应地址信息。(注:在μcore里面,所有段的基址为0,长度为4g)

段寄存器(cs等)高十三位存储gdt的index,最低两位(rp)表示优先级,第十四位为ldt标志位,表示是否使用ldt(本地描述符表)

3

将控制寄存器(CR0)第0号bit置为1,这样cpu就会进入保护模式。

4

加载elf格式的μcore os kernel,将相应的代码段数据段等信息读入内存。

c函数调用

内联汇编

c语言不能利用所有的汇编指令,故使用内联汇编

volatile:不需要优化

%0:第一个用到的寄存器

r:任意寄存器

a:eax

b:ebx

S:esi

D:edi

中断处理

中断或异常斗鱼一个中断服务例程关联,保存此关联信息的表称之为中断描述符表(IDT),其起始地址以及大小保存在中断描述符IDTR中。

中断处理时需要考虑特权级变化(通过段描述符的特权值位)

iret:弹出eflags和ss,esp

ret:eip(近跳转)

retf:弹出cs和eip(远)

通过中断处理实现系统调用

指定中断号,使用trap,特殊指令sysenter/sysexit)

练习

make V= 可以把编译时具体过程打印出来

特征标记:tools/sign.c

符合扇区规范的扇区标准:大小小于510,最后两个字节分别为0x55与0xAA(结束标志)

   cli                                             # Disable interrupts
    cld                                             # String operations increment

    # Set up the important data segment registers (DS, ES, SS).
    xorw %ax, %ax                                   # Segment number zero
    movw %ax, %ds                                   # -> Data Segment
    movw %ax, %es                                   # -> Extra Segment
    movw %ax, %ss                                   # -> Stack Segment

    # Enable A20:
    #  For backwards compatibility with the earliest PCs, physical
    #  address line 20 is tied low, so that addresses higher than
    #  1MB wrap around to zero by default. This code undoes this.
seta20.1:
    inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).
    testb $0x2, %al
    jnz seta20.1

    movb $0xd1, %al                                 # 0xd1 -> port 0x64
    outb %al, $0x64                                 # 0xd1 means: write data to 8042's P2 port

seta20.2:
    inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).
    testb $0x2, %al
    jnz seta20.2

    movb $0xdf, %al                                 # 0xdf -> port 0x60
    outb %al, $0x60                                 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1

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

    # Jump to next instruction, but in 32-bit code segment.
    # Switches processor into 32-bit mode.
    ljmp $PROT_MODE_CSEG, $protcseg

inb指令:从端口向目标读入一个字节

首先不断地从0x64端口读入字节,直到为空

而后向0x64端口写0xdl(0xd1 means: write data to 8042’s P2 port)

不断地从0x64端口读入字节,直到为空

向0x60端口写入0xdf(0xdf = 11011111, means set P2’s A20 bit(the 1 bit) to 1)

lgdt:加载gdt

保存将cr0低字节置为pe-on状态,跳转。

加载内核文件

readsect(void *dst, uint32_t secno) { // wait for disk to be ready waitdisk();

outb(0x1F2, 1);                         // count = 1
outb(0x1F3, secno & 0xFF);
outb(0x1F4, (secno >> 8) & 0xFF);
outb(0x1F5, (secno >> 16) & 0xFF);
outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);
outb(0x1F7, 0x20);                      // cmd 0x20 - read sectors

// wait for disk to be ready
waitdisk();

// read a sector
insl(0x1F0, dst, SECTSIZE / 4); | 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端口读数据 |

就是传参以及设置为主盘,一个扇区,然后读取数据,大小为四分之一个扇区大小(128)