欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于ucore的操作系统实验lab1

程序员文章站 2022-06-19 16:42:03
...

基于ucore的操作系统实验lab1

[练习1]

练习1.1

1、先编译链接生成kernel文件
2、再编译链接生成bootblock文件
3、生成ucore.imgsign.c生成bootblock
1.2 查看sign.c可以发现关键在于bootblock.out小于510bytes,且 buf[510] = 0x55; buf[511] = 0xAA;

[练习2]

练习2.1 从 CPU 加电后执行的第一条指令开始,单步跟踪 BIOS 的执行。

单步跟踪,方法如下:

1 修改 lab1/tools/gdbinit,内容为:


set architecture
i8086

target remote
:1234

2 在 lab1目录下,执行


make debug

3 在看到gdb的调试界面(gdb)后,在gdb调试界面下执行如下命令


si

即可单步跟踪BIOS了。

4 在gdb界面下,可通过如下命令来看BIOS的代码


 x /2i $pc 
//显示当前eip处的汇编指令

练习2.2 在初始化位置0x7c00设置实地址断点,测试断点正常。

在gdb中输入一下命令

    b *0x7c00 
//在0x7c00处设置断点。此地址是bootloader入口点地址,可看boot/bootasm.S的start地址处

    c         
//continue简称,表示继续执行

    x /2i $pc 
//显示当前eip处的汇编指令

得到
Breakpoint 2,
0x00007c00 in ?? ()

=> 0x7c00:      cli   


   0x7c01:      cld 

练习2.3 从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较。

将2.2中的步骤重复实现与两个文件进行对比发现是一致的

练习2.4 自己找一个bootloader或内核中的代码位置,设置断点并进行测试。

自己按以上步骤测试一下就行了

练习3

练习3.1 分析bootloader进入保护模式的过程。

它先将各个寄存器置0

movw $PROT_MODE_DSEG, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
movl $0x0, %ebp
movl $start, %esp

初始化GDT表:一个简单的GDT表和其描述符已经静态储存在引导区中,载入即可


       
lgdt gdtdesc

进入保护模式:通过将cr0寄存器PE位置1便开启了保护模式


       
movl %cr0, %eax

       
orl $CR0_PE_ON, %eax

       
movl %eax, %cr0

通过长跳转更新cs的基地址


     ljmp
$PROT_MODE_CSEG, $protcseg

    .code32

    protcseg:

设置段寄存器,并建立堆栈


       
movw $PROT_MODE_DSEG, %ax

       
movw %ax, %ds

       
movw %ax, %es

       
movw %ax, %fs

       
movw %ax, %gs

       
movw %ax, %ss

       
movl $0x0, %ebp

       
movl $start, %esp

转到保护模式完成,进入boot主方法


       
call bootmain

练习4 分析bootloader加载ELF格式的OS的过程。

首先看readsect函数,

readsect从设备的第secno扇区读取数据到dst位置


       static
void

       readsect(void
*dst, uint32_t secno) {

           waitdisk();

       

           outb(0x1F2, 1);                         // 设置读取扇区的数目为1

           outb(0x1F3, secno & 0xFF);

           outb(0x1F4, (secno >> 8) & 0xFF);

           outb(0x1F5, (secno >> 16) &
0xFF);

           outb(0x1F6, ((secno >> 24) & 0xF)
| 0xE0);

               // 上面四条指令联合制定了扇区号

               // 在这4个字节线联合构成的32位参数中

               //  
29-31位强制设为1

               //  
28位(=0)表示访问"Disk
0"

               //  
0-27位是28位的偏移量

           outb(0x1F7, 0x20);                      // 0x20命令,读取扇区

       

           waitdisk();

 

           insl(0x1F0, dst, SECTSIZE / 4);         // 读取到dst位置,

                                                   // 幻数4因为这里以DW为单位

       }

readseg简单包装了readsect,可以从设备读取任意长度的内容。


       static
void

       readseg(uintptr_t
va, uint32_t count, uint32_t offset) {

           uintptr_t end_va = va + count;

       

           va -= offset % SECTSIZE;

       

           uint32_t secno = (offset / SECTSIZE) + 1; 

           // 加1因为0扇区被引导占用

           // ELF文件从1扇区开始

       

           for (; va < end_va; va += SECTSIZE,
secno ++) {

               readsect((void *)va, secno);

           }

       }

在bootmain函数中,


       void

       bootmain(void)
{

           // 首先读取ELF的头部

           readseg((uintptr_t)ELFHDR, SECTSIZE * 8,
0);

       

           // 通过储存在头部的幻数判断是否是合法的ELF文件

           if (ELFHDR->e_magic != ELF_MAGIC) {

               goto bad;

           }

       

           struct proghdr *ph, *eph;

       

           // ELF头部有描述ELF文件应加载到内存什么位置的描述表,

           // 先将描述表的头地址存在ph

           ph = (struct proghdr *)((uintptr_t)ELFHDR +
ELFHDR->e_phoff);

           eph = ph + ELFHDR->e_phnum;

       

           // 按照描述表将ELF文件中数据载入内存

           for (; ph < eph; ph ++) {

               readseg(ph->p_va & 0xFFFFFF,
ph->p_memsz, ph->p_offset);

           }

           // ELF文件0x1000位置后面的0xd1ec比特被载入内存0x00100000

           // ELF文件0xf000位置后面的0x1d20比特被载入内存0x0010e000

 

           // 根据ELF头部储存的入口信息,找到内核的入口

           ((void (*)(void))(ELFHDR->e_entry &
0xFFFFFF))();

       

       bad:

           outw(0x8A00, 0x8A00);

           outw(0x8A00, 0x8E00);

           while (1);

       }

练习5

实现函数调用堆栈跟踪函数

ss:ebp指向的堆栈位置储存着caller的ebp,以此为线索可以得到所有使用堆栈的函数ebp。

ss:ebp+4指向caller调用时的eip,ss:ebp+8等是(可能的)参数。

输出中,堆栈最深一层为


       ebp:0x00007bf8
eip:0x00007d68 \

              args:0x00000000
0x00000000 0x00000000 0x00007c4f

           <unknow>: -- 0x00007d67 --