练习一:
执行 make "V="的结果。
zhh043@moocos-VirtualBox-> make "V="
+ cc kern/init/init.c
gcc -Ikern/init/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.o
kern/init/init.c:95:1: warning: ‘lab1_switch_test’ defined but not used [-Wunused-function]
lab1_switch_test(void) {
^
+ cc kern/libs/readline.c
gcc -Ikern/libs/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/readline.c -o obj/kern/libs/readline.o
+ cc kern/libs/stdio.c
gcc -Ikern/libs/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/stdio.c -o obj/kern/libs/stdio.o
+ cc kern/debug/kdebug.c
gcc -Ikern/debug/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kdebug.c -o obj/kern/debug/kdebug.o
kern/debug/kdebug.c:251:1: warning: ‘read_eip’ defined but not used [-Wunused-function]
read_eip(void) {
^
+ cc kern/debug/kmonitor.c
gcc -Ikern/debug/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kmonitor.c -o obj/kern/debug/kmonitor.o
+ cc kern/debug/panic.c
gcc -Ikern/debug/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/panic.c -o obj/kern/debug/panic.o
kern/debug/panic.c: In function ‘__panic’:
kern/debug/panic.c:27:5: warning: implicit declaration of function ‘print_stackframe’ [-Wimplicit-function-declaration]
print_stackframe();
^
+ cc kern/driver/clock.c
gcc -Ikern/driver/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/clock.c -o obj/kern/driver/clock.o
+ cc kern/driver/console.c
gcc -Ikern/driver/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/console.c -o obj/kern/driver/console.o
+ cc kern/driver/intr.c
gcc -Ikern/driver/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/intr.c -o obj/kern/driver/intr.o
+ cc kern/driver/picirq.c
gcc -Ikern/driver/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/picirq.c -o obj/kern/driver/picirq.o
+ cc kern/trap/trap.c
gcc -Ikern/trap/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trap.c -o obj/kern/trap/trap.o
kern/trap/trap.c:14:13: warning: ‘print_ticks’ defined but not used [-Wunused-function]
static void print_ticks() {
^
kern/trap/trap.c:30:26: warning: ‘idt_pd’ defined but not used [-Wunused-variable]
static struct pseudodesc idt_pd = {
^
+ cc kern/trap/trapentry.S
gcc -Ikern/trap/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trapentry.S -o obj/kern/trap/trapentry.o
+ cc kern/trap/vectors.S
gcc -Ikern/trap/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/vectors.S -o obj/kern/trap/vectors.o
+ cc kern/mm/pmm.c
gcc -Ikern/mm/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/mm/pmm.c -o obj/kern/mm/pmm.o
+ cc libs/printfmt.c
gcc -Ilibs/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -c libs/printfmt.c -o obj/libs/printfmt.o
+ cc libs/string.c
gcc -Ilibs/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -c libs/string.c -o obj/libs/string.o
+ ld bin/kernel
ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o obj/kern/libs/readline.o obj/kern/libs/stdio.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/debug/panic.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/intr.o obj/kern/driver/picirq.o obj/kern/trap/trap.o obj/kern/trap/trapentry.o obj/kern/trap/vectors.o obj/kern/mm/pmm.o obj/libs/printfmt.o obj/libs/string.o
+ cc boot/bootasm.S
gcc -Iboot/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
+ cc boot/bootmain.c
gcc -Iboot/ -march=i686 -fno-builtin -fno-PIC -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
+ cc tools/sign.c
gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
+ ld bin/bootblock
ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
'obj/bootblock.out' size: 472 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
dd if=/dev/zero of=bin/ucore.img count=10000
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB) copied, 0.0290902 s, 176 MB/s
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000174016 s, 2.9 MB/s
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
146+1 records in
146+1 records out
74871 bytes (75 kB) copied, 0.000366798 s, 204 MB/s
[~/Desktop/ucore_os_lab-master/labcodes/lab1]
可以看到,它先是将一些C的源码通过gcc编译成.o文件,然后用ld将.o转换成可执行文件。
主要是需要 bootloader 和 kernel 两个程序。
这其实就展示了ucore是如何一步步生成的。
然后查看了tools目录下的sign.c看到其中有:
buf[510]=0x55;
buf[511]=0xAA;
这两个就是符合规范的硬盘主引导扇区的特征
分别是:
1.输入的主引导扇区的记录必须是510字节以内(446+64)。
2.输出的主引导扇区的最后两个字节是55AA。
练习2:
按照实验书,看到Makefile里的lab1-mon指令:
201 lab1-mon: $(UCOREIMG)
202 $(V)$(TERMINAL) -e "$(QEMU) -S -s -d in_asm -D $(BINDIR)/q.log -monitor stdio -hda $< -serial null"
203 $(V)sleep 2
204 $(V)$(TERMINAL) -e "gdb -q -x tools/lab1init"
其实这些就是一些初始化的执行指令。
然后主要是lab1init:
1 file bin/kernel #加载bin/kernel
2 target remote :1234 #与qemu连接
3 set architecture i8086 #bios进入8086实模式
4 b *0x7c00 #在0x7c00设置断点
5 continue #系统继续运行
6 x /2i $pc #显示pc
执行 make lab1-mon:
可以看到在0x7c00处断开了。
然后continue让它继续运行:看到可以继续运行了。
练习三:
看一下bootasm.S的源码:
1 #include <asm.h>
2
3 # Start the CPU: switch to 32-bit protected mode, jump into C.
4 # The BIOS loads this code from the first sector of the hard disk into
5 # memory at physical address 0x7c00 and starts executing in real mode
6 # with %cs=0 %ip=7c00.
7
8 .set PROT_MODE_CSEG, 0x8 # kernel code segment selector
9 .set PROT_MODE_DSEG, 0x10 # kernel data segment selector
10 .set CR0_PE_ON, 0x1 # protected mode enable flag
11
12 # start address should be 0:7c00, in real mode, the beginning address of the running bootloader
13 .globl start
14 start:
15 .code16 # Assemble for 16-bit mode
16 cli # Disable interrupts
17 cld # String operations increment
18
19 # Set up the important data segment registers (DS, ES, SS).
20 xorw %ax, %ax # Segment number zero
21 movw %ax, %ds # -> Data Segment
22 movw %ax, %es # -> Extra Segment
23 movw %ax, %ss # -> Stack Segment
24
25 # Enable A20:
26 # For backwards compatibility with the earliest PCs, physical
27 # address line 20 is tied low, so that addresses higher than
28 # 1MB wrap around to zero by default. This code undoes this.
29 seta20.1:
30 inb $0x64, %al # Wait for not busy(8042 input buffer empty).
31 testb $0x2, %al
32 jnz seta20.1
33
34 movb $0xd1, %al # 0xd1 -> port 0x64
35 outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port
36
37 seta20.2:
38 inb $0x64, %al # Wait for not busy(8042 input buffer empty).
39 testb $0x2, %al
40 jnz seta20.2
41
42 movb $0xdf, %al # 0xdf -> port 0x60
43 outb %al, $0x60 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 43 1
44
45 # Switch from real to protected mode, using a bootstrap GDT
46 # and segment translation that makes virtual addresses
47 # identical to physical addresses, so that the
48 # effective memory map does not change during the switch.
49 lgdt gdtdesc
50 movl %cr0, %eax
51 orl $CR0_PE_ON, %eax
52 movl %eax, %cr0
53
54 # Jump to next instruction, but in 32-bit code segment.
55 # Switches processor into 32-bit mode.
56 ljmp $PROT_MODE_CSEG, $protcseg
57
58 .code32 # Assemble for 32-bit mode
59 protcseg:
60 # Set up the protected-mode data segment registers
61 movw $PROT_MODE_DSEG, %ax # Our data segment selector
62 movw %ax, %ds # -> DS: Data Segment
63 movw %ax, %es # -> ES: Extra Segment
64 movw %ax, %fs # -> FS
65 movw %ax, %gs # -> GS
66 movw %ax, %ss # -> SS: Stack Segment
67
68 # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)
69 movl $0x0, %ebp
70 movl $start, %esp
71 call bootmain
72
73 # If bootmain returns (it shouldn't), loop.
74 spin:
75 jmp spin
76
77 # Bootstrap GDT
78 .p2align 2 # force 4 byte alignment
79 gdt:
80 SEG_NULLASM # null seg
81 SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel
82 SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel
83
84 gdtdesc:
85 .word 0x17 # sizeof(gdt) - 1
86 .long gdt # address gdt
其实BootLoader主要做了以下几件事:
- 关闭中断
- A20 使能
- 全局描述符表初始化
- 保护模式启动
- 设置段寄存器(长跳转更新CS,根据设置好的段选择子更新其他段寄存器)
- 设置堆栈,esp 0x700 ebp 0
- 进入bootmain后读取内核映像到内存,检查是否合法,并启动操作系统,控制权交给它
练习四:
查看kernel文件:
moocos-> file kernel
kernel: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
就是它完成了上个实验中的一些基础功能。
BootLoader读取硬盘扇区其实是通过一个函数来完成的:
就是图中的readseg
它用的是一种IO的地址寻址,将外设的数据读到内存,也就是读取硬盘扇区。
在readseg的下方,有一个对于ELF格式的判断
它是通过读取了ELF的HEADER,然后通过判断特殊成员变量 e magic 是否为一个特定的值来判断是否为合法格式的ELF文件。
练习五:
执行make qemu 结果如下:
练习六:
中断向量表一个表项占用8字节,2、3字节是段选择子,0、1字节和6、7字节拼成位移, 两者联合便是中断处理程序的入口地址。
idt_init:
idt_init(void) {
extern uintptr_t __vectors[];
for (int i = 0; i < 256; ++i) {
SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
}
SETGATE(idt[T_SWITCH_TOK], 0, GD_KTEXT, __vectors[T_SWITCH_TOK], DPL_USER);
lidt(&idt_pd);
}
ticks++;
if (ticks== TICK_NUM) {
ticks= 0;
print_ticks();
}
break;