操作系统实践(3)——火箭助推器
来源:互联网 发布:成绩分析软件 编辑:程序博客网 时间:2024/05/15 02:19
本次实践的目的:打破开机引导程序512字节的限制,并从实模式切换到保护模式。
我们知道,bios开机自检、找到启动设备后,把启动设备的第一个扇区加载内存0x7c00位置开始执行。前两次实践中,我们的引导程序小于512字节,这没造成什么问题。如果我们的引导程序超过512字节怎么办呢?我的第一个想法就是,利用加载到内存的这512字节,写个程序,把启动盘中真正的引导程序继续加载到内存中。看到《Orange’s 一个操作系统的实现》的第三章的时候,里面并没有采用这种做法,而是转而去用个DOS加载大于512字节的引导程序,那就自己动手写一个吧。
程序思路
- 写个少于512字节的引导程序,用于启动引导后,把软盘第2扇区(如果你写的代码多与512字节,需要修改下面的引导,这里简单只拷贝了1个扇区)的数据拷贝到 0x7e00 的位置(0x7e00 == 0x7c00 + 512)。具体实现是调用了bios的13h中断。
- 保护模式的寻址方式与实模式的寻址方式不同。 虽然从实模式到保护模式只需要设置cr0寄存器即可,但是切换过去后,其寻址依赖于GDT的实现,所以需切换前先设置好GDT。
bf.asm
org 07c00h [BITS 16]START: mov ax,cs mov ds,ax mov es,ax ;拷贝软盘中的代码到内存区COPY: mov bx, COPY_CODE_START ;07c00h + 512(0100h) == 07e00h mov dl,0 ;驱动器号,软驱从0开始:0:软驱A,1:软驱B ;磁盘从80h开始,80h:C盘,81h:D盘 mov dh,0 ;磁头号,对于软盘即面号,一个面用一个磁头来读写 mov ch,0 ;磁道号 mov cl,2 ;扇区号 mov al,2 ;读取的扇区数 mov ah,2 ;13h的功能号(2表示读扇区),es:bx指向 ;接收从扇区读入数据的内存区 int 13h jc COPY ;读取失败,CF表示为1,重试读取 jmp LABEL_BEGIN ;把程序读到内存区后,跳转到新的执行点 ;补全512字节 times 510-($-$$) db 0 dw 0xaa55;这个宏用来填充gdt描述符的,每个描述符8个字节,64位。;参数1:段基址,32位;参数2:段大小limit,传32位,只用其低20位。;参数3:段属性,16位,只用高4位与低8位,中间4位为0。%macro Descriptor 3 dw %2 & 0FFFFh dw %1 & 0FFFFh db (%1 >> 16) & 0FFh dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) db (%1 >> 24) & 0FFh%endmacro;段属性的常量,具体参考《Orange's 一个操作系统的实现》DA_32 equ 4000hDA_DPL0 equ 00hDA_DPL1 equ 20hDA_DPL2 equ 40hDA_DPL3 equ 60hDA_DR equ 90hDA_DRW equ 92hDA_DRWA equ 93hDA_C equ 98hDA_CR equ 9ahDA_CCO equ 9chDA_CCOR equ 9ehDA_LDT equ 82hDA_TaskGate equ 85hDA_386TSS equ 89hDA_386CGate equ 8chDA_386IGate equ 8ehDA_386TGate equ 8fhCOPY_CODE_START:;全局描述符GDT,在切换到保护模式前,需先设置好相应的描述符。[SECTION .gdt]LABEL_GDT: Descriptor 0, 0, 0LABEL_DESC_CODE32: Descriptor 0,SegCode32Len - 1, DA_C + DA_32LABEL_DESC_VIDEO: Descriptor 0b8000h, 0ffffh,DA_DRWGdtLen equ $ - LABEL_GDTGdtPtr dw GdtLen - 1 dd 0SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDTSelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT;这段16位的代码段,目的是实现从实模式到保护模式的切换。[SECTION .s16][BITS 16]LABEL_BEGIN: mov ax,cs mov ds,ax mov es,ax mov ss,ax mov sp,0100h ;设置好进入保护模式后立刻要执行的代码段的描述符 xor eax,eax mov ax,cs shl eax,4 add eax,LABEL_SEG_CODE32 mov word [LABEL_DESC_CODE32 + 2],ax shr eax,16 mov byte [LABEL_DESC_CODE32 + 4],al mov byte [LABEL_DESC_CODE32 + 7],ah ;设置GDT xor eax,eax mov ax,ds shl eax,4 add eax,LABEL_GDT mov dword [GdtPtr + 2],eax ;加载gdt lgdt [GdtPtr] cli ;打开A20地址线,扩大寻址空间 in al,92h or al,00000010b out 92h,al ;从实模式切换到保护模式 mov eax,cr0 or eax,1 mov cr0,eax ;跳转到32位的保护模式的代码 jmp dword SelectorCode32:0;这段代码的功能,只是在屏幕右边的中间位置显示一个黑底红色的字母'P'[SECTION .s32][BITS 32]LABEL_SEG_CODE32: mov ax,SelectorVideo mov gs,ax mov edi,(80 * 11 + 79) *2 mov ah,0ch mov al,'P' mov [gs:edi],ax jmp $SegCode32Len equ $ - LABEL_SEG_CODE32
这里为了省去每次都敲一堆命令的麻烦,写了个简单的脚本
bf.sh
#!/bin/bash/usr/bin/nasm bf.asm -o bf.bindd if=bf.bin of=bf.img bs=512 count=2 conv=notruncbochs -f bf.bochs #这里的bf.bochs配置文件,请参考前一节的配置
执行 ./bf.sh
结果下图(屏幕右边中间位置有个红色的’P’):
问题
gdt、gdtr结构如何?
这个虽然在不同书籍都看过了,没亲自写代码,还是会忘掉。网上的版本请参考: 《GDT 与 LDT》。gdtr limit字段如何设置?为什么用gdt长度减一?
参考gdt的limit字段,其实不应该理解为长度,而是与offset类似的,从0开始,比如说计算gdtr最高的地址的时候,就可以用基址+limit计算出来。打开a20线还有其它方法吗?
这个问题还没搞清楚,回头更新这里。在16位模式下jmp dword SelectorCode32:0 其中的dword 是修饰哪个?linux内核中用db写二进制是如何实现的?
按目前的理解,选择子只有13位有效,所以 SelectorCode32这个用16位足以,而后面的偏移量offset,则可以是32位的。这句代码反汇编之后结果为:
00007e71: 66ea 0000 0000 0800 jmpf 0x0008:0000 0000
至于linux内核如何实现,后续跟进。我猜想就是直接用类似上面的二进制代码方式实现。
- 操作系统实践(3)——火箭助推器
- 火箭助推器已点火 英镑:谁也别拦我!我要飞离外太空!
- python编程从入门到实践 习题12-3 火箭
- 开发操作系统实践(二)——LinDos文件系统
- 操作系统实践(1)——从引导开始
- 操作系统实践(2)——工欲善其事,必先利其器
- 操作系统实践(5)——关于LDT
- 操作系统实践(6)——特权级转移
- 操作系统实践(7)——分页机制
- 操作系统实践(8)——从Loader到内核
- 代表着尖端科技的火箭助推器的宽度,竟然被两匹马的屁股的总宽度决定了
- 转杨毅:火箭——自助者天助
- 火箭翠西传(精彩)
- 火箭
- 操作系统3本实践书
- 操作系统实验(geekOS实践)
- 《大话操作系统——做坚实的工程实践派》(3)
- 开发操作系统实践(三)——LinDos引导扇区内容及运行环境搭建
- 第三方登录 QQ WEIBO
- python模拟登陆人人网(通过BeautifulSoup module)
- Android SimpleAdapter显示ListView、GridView
- CentOS6.5菜鸟之旅:文件权限详解
- 以HttpGet方法获取URL数据并转换成指定编码格式的字符串
- 操作系统实践(3)——火箭助推器
- 框架嵌套实例
- HDU BC#39 1002 Mutiple
- set集合容器
- wiki搭建--apache+mysql+php+wiki
- 结构体struct
- 【算法导论】第十一课 扩充的数据结构、动态有序统计和区间树
- OOM问题 解决原则
- C Primer Plus 第九章 学习总结……2015.4.26