操作系统课程实验 Lab1:bootloader 启动 ucore os
大纲 x86 启动顺序 C 函数调用 gcc 内联汇编 (inline assembly) x86-32 下的中断处理
理解 x86-32 平台的启动过程理解 x86-32 的实模式 保护模式理解段机制 x86 启动顺序
x86 启动顺序 寄存器初始值 摘自 "IA-32 Intel 体系结构软件开发者手册 "
x86 启动顺序 第一条指令 CS = F000H, EIP = 0000FFF0H 实际地址是 : Base + EIP = FFFF0000H + 0000FFF0H = FFFFFFF0H 这是 BIOS 的 EPROM (Erasable Programmable Read Only Memory) 所在地 当 CS 被新值加载, 则地址转换规则将开始起作用 通常第一条指令是一条长跳转指令 ( 这样 CS 和 EIP 都会更新 ) 到 BIOS 代码中执行
x86 启动顺序 处于实模式的段 摘自 "IA-32 Intel 体系结构软件开发者手册 " 段选择子 (Segment Selector): CS, DS, SS, 偏移量 (Offset): EIP
x86 启动顺序 从 BIOS 到 Bootloader BIOS 加载存储设备 ( 比如软盘 硬盘 光盘 USB 盘 ) 上的第一个扇区 ( 主引导扇区,Master Boot Record, or MBR) 的 512 字节到内存的 0x7c00 然后转跳到 @ 0x7c00 的第一条指令开始执行
x86 启动顺序 从 bootloader 到 OS bootloader 做的事情 : 使能保护模式 (protection mode) & 段机制 (segmentlevel protection) 从硬盘上读取 kernel in ELF 格式的 ucore kernel ( 跟在 MBR 后面的扇区 ) 并放到内存中固定位置跳转到 ucore OS 的入口点 (entry point) 执行, 这时控制权到了 ucore OS 中
x86 启动顺序 段机制 摘自 "IA-32 Intel 体系结构软件开发者手册 "
x86 启动顺序 段机制 摘自 "IA-32 Intel 体系结构软件开发者手册 "
x86 启动顺序 段机制 Loading GDT: lgdt gdtdesc gdt: gdtdesc:.word 0x17.long gdt 摘自 "IA-32 Intel 体系结构软件开发者手册 "
x86 启动顺序 使能保护模式 摘自 "IA-32 Intel 体系结构软件开发者手册 " 使能保护模式 (protection mode), bootloader/os 要设置 CR0 的 bit 0 (PE) 段机制 (Segment-level protection) 在保护模式下是自动使能的
x86 启动顺序 加载 ELF 格式的 ucore OS kernel struct elfhdr { uint magic; // must equal ELF_MAGIC uchar elf[12]; ushort type; ushort machine; uint version; uint entry; // program entry point (in va) uint phoff; // offset of the program header tables uint shoff; uint flags; ushort ehsize; ushort phentsize; ushort phnum; // number of program header tables ushort shentsize; ushort shnum; ushort shstrndx; };
x86 启动顺序 加载 ELF 格式的 ucore OS kernel struct proghdr { uint type; uint offset; uint va; uint pa; uint filesz; uint memsz; uint flags; uint align; }; // segment type // beginning of the segment in the file // where this segment should be placed at // size of the segment in byte
x86 启动顺序 参考资料 Chap. 2.5 (Control Registers) ), Vol. 3, Intel and IA-32 Architectures Software Developer s Manual Chap. 3 (Protected-Mode Memory Management), Vol. 3, Intel and IA-32 Architectures Software Developer s Manual Chap. 9.l (Initialization Overview), Vol. 3, Intel and IA-32 Architectures Software Developer s Manual An introduction to ELF format: http://wiki.osdev.org/elf
理解 C 函数调用在汇编级是如何实现的理解如何在汇编级代码中调用 C 函数理解基于 EBP 寄存器的函数调用栈 C 函数调用的实现
C 函数调用的实现
C 函数调用的实现
C 函数调用的实现
C 函数调用的实现
C 函数调用的实现
C 函数调用的实现
C 函数调用的实现
C 函数调用的实现
C 函数调用的实现 其他需要注意的事项 参数 (parameters) & 函数返回值 (return values) 可通过寄存器或位于内存中的栈来传递 不需要保存 / 恢复 (save/restore) 所有寄存器
C 函数调用的 参考资料 Understanding the Stack: http://www.cs.umd.edu/class/sum2003/cmsc311/notes/mips/stack.html
阅读理解内联汇编 (inline assembly instructions) GCC 内联汇编 INLINE ASSEMBLY
CC 内联汇编 什么是内联汇编 ( Inline assembly )? 这是 GCC 对 C 语言的扩张 可直接在 C 语句中插入汇编指令 有何用处? 调用 C 语言不支持的指令 用汇编在 C 语言中手动优化 如何工作? 用给定的模板和约束来生成汇编指令 在 C 函数内形成汇编源码
CC 内联汇编 Example 1 Assembly (*.S): movl $0xffff, %eax Inline assembly (*.c): asm ( movl $0xffff, %%eax\n )
CC 内联汇编 Syntax asm ( assembler template : output operands (optional) : input operands (optional) : clobbers (optional) );
CC 内联汇编 Example 2 Inline assembly (*.c): uint32_t cr0; asm volatile ("movl %%cr0, %0\n" :"=r"(cr0)); cr0 = 0x80000000; asm volatile ("movl %0, %%cr0\n" ::"r"(cr0)); Generated asssembly code (*.s): movl %cr0, %ebx movl %ebx, 12(%esp) orl $-2147483648, 12(%esp) movl 12(%esp), %eax movl %eax, %cr0
CC 内联汇编 Example 2 Inline assembly (*.c): uint32_t cr0; asm volatile ("movl %%cr0, %0\n" :"=r"(cr0)); cr0 = 0x80000000; asm volatile ("movl %0, %%cr0\n" ::"r"(cr0)); volatile No reordering; No elimination %0 The first constraint following r A constraint; GCC is free to use any register
CC 内联汇编 Example 3 long res, arg1 = 2, arg2 = 22, arg3 = 222, arg4 = 233; asm volatile ("int $0x80" : "=a" ( res) : "0" (11),"b" (arg1),"c" (arg2),"d" (arg3),"s" (arg4)); movl $11, %eax movl -28(%ebp), %ebx movl -24(%ebp), %ecx movl -20(%ebp), %edx movl -16(%ebp), %esi int $0x80 movl %eax, -12(%ebp) Constraints a = %eax b = %ebx c = %ecx d = %edx S = %esi D = %edi 0 = same as the first
CC 内联汇编 - 参考资料 GCC Manual 6.41 6.43 Inline assembly for x86 in Linux: http://www.ibm.com/developerworks/library/l-ia/index.html
了解 x86 中的中断源了解 CPU 与操作系统如何处理中断能够对中断向量表 ( 中断描述符表, 简称 IDT) 进行初始化 X86 中的中断处理
中断 Interrupts 外部中断 External (hardware generated) interrupts 串口 硬盘 网卡 时钟 软件产生的中断 Software generated interrupts The INT n 指令, 通常用于系统调用 异常 Exceptions X86 中的中断处理 中断源 程序错误软件产生的异常 Software generated exceptions INTO, INT 3 and BOUND 机器检查出的异常 S
X86 中的中断处理 确定中断服务例程 (ISR) 每个中断或异常与一个中断服务例程 ( Interrupt Service Routine, 简称 ISR) 关联, 其关联关系存储在中断描述符表 ( Interrupt Descriptor Table, 简称 IDT) IDT 的起始地址和大小保存在中断描述符表寄存器 IDTR 中 摘自 "IA-32 Intel 体系结构软件开发者手册 "
X86 中的中断处理 确定中断服务例程 (ISR) 摘自 "IA-32 Intel 体系结构软件开发者手册 "
X86 中的中断处理 确定中断服务例程 (ISR) 摘自 "IA-32 Intel 体系结构软件开发者手册 "
X86 中的中断处理 切换到中断服务例程 (ISR) 不同特权级的中断切换对堆栈的影响 High Low EFLAGS CS EIP Error Code Stack Usage with No Privilege-Level Change ESP Before Transfer to Handler ESP After Transfer to Handler Stack Usage with Privilege-Level Change ESP Before Transfer to Handler ESP After Transfer to Handler SS ESP EFLAGS CS EIP Error Code Stack 1 Stack 1 Stack 2
X86 中的中断处理 从中断服务例程 (ISR) 返回 iret vs. ret vs. retf : iret 弹出 EFLAGS 和 SS/ESP( 根 据是否改变特权级 ), 但 ret 弹出 EIP, retf 弹出 CS 和 EIP Stack Usage with No Privilege-Level Change Stack Usage with Privilege-Level Change High EFLAGS ESP After Transfer to Handler SS ESP After Transfer to Handler SS CS ESP ESP EIP Error Code ESP Before Transfer to Handler EFLAGS CS EFLAGS CS EIP Error Code ESP Before Transfer to Handler EIP Error Code Low
X86 中的中断处理 系统调用 用户程序通过系统调用访问 OS 内核服务 如何实现 需要指定中断号使用 Trap, 也称 Software generated interrupt 或使用特殊指令 (SYSENTER/SYSEXIT)
X86 中的中断处理 参考资料 Chap. 6, Vol. 3, Intel and IA-32 Architectures Software Developer s Manual