哈工大操作系统实验(一)操作系统启动

tech2024-11-24  20

目录

实验背景1. 实模式2. 实模式地址3. 启动流程 实验目的实验内容实验报告1. bootsect.s 程序2. setup.s 程序 参考资料

实验背景

1. 实模式

实模式 是80286及以上的微处理器采用8086的工作模式,从加电启动或复位到操作系统运行之前的运行环境。保护模式 是80386及以上的微处理器的主要工作模式,支持多任务、设置特权级、特权指令执行等,操作系统和应用程序的运行环境。因为操作系统启动处于实模式,所以以下仅涉及实模式下的寄存器和物理地址生成。

2. 实模式地址

① 指令指针寄存器 IP 保存一个内存地址,指向当前需要取出的指令 ② 段寄存器与其它寄存器联合生成存储器地址,包括代码段寄存器 CS、数据段寄存器 DS、附加段寄存器 ES 和堆栈段寄存器 SS

指令寄存器 IP 为16位寄存器,寻址能力为 2 16 = 64 K B 2^{16}=64KB 216=64KB;8086对外有20位地址线,寻址范围是 2 20 = 1 M B 2^{20}=1MB 220=1MB 。1MB的物理存储空间分成许多逻辑段,每段最长 64 K B 64KB 64KB,从而可用16位地址寻址。

实模式下给出逻辑地址 段基值:偏移量,十进制物理地址计算方法是 物理地址=段基值×16+偏移量,二进制物理地址计算方法是 物理地址=段基址<<4+偏移量

BIOS运行在实模式下,段基值 CS 为 0xF000, 偏移量 IP 设置为 0xFFF0,从而组成BIOS入口地址 0xFFFF0。

3. 启动流程

加电稳定后,CPU进入实模式并从 BIOS引导程序 入口地址 0XFFFF0 开始自动执行代码。BIOS引导程序 初始化硬件并在物理地址0处放置大小为1KB的中断向量表,它将 磁盘引导块程序 bootsect.s 读入内存地址 0X7C00,CPU执行跳转指令跳转到该处执行。 磁盘引导块程序 bootsect.s 执行时把自己移动到0X90000 处,同时将 操作系统加载程序 setup.s 加载到内存 0X90200 处,操作系统内核 system 模块加载到内存 0X10000 开始的地方。

操作系统加载程序 setup.s 利用BIOS中断读取机器系统参数,并将参数覆盖保存 bootsect.s 的位置,同时将 system 模块整体向下移动到 0X0000 处覆盖BISO中断。最后,CPU进入保护模式,跳转到 system 模块前端的 head.s 继续运行,从而成功启动操作系统。

实验目的

熟悉hit-oslab实验环境;建立对操作系统引导过程的深入认识;掌握操作系统的基本开发过程;能对操作系统代码进行简单的控制,揭开操作系统的神秘面纱。

实验内容

此次实验的基本内容是:

阅读《Linux内核完全注释》的第6章,对计算机和Linux 0.11的引导过程进行初步的了解;按照下面的要求改写0.11的引导程序bootsect.s有兴趣同学可以做做进入保护模式前的设置程序setup.s。

实验报告

1. bootsect.s 程序

改写bootsect.s主要完成如下功能:

bootsect.s能在屏幕上打印一段提示信息“XXX is booting…”,其中XXX是你给自己的操作系统起的名字,例如LZJos、Sunix等(可以上论坛上秀秀谁的OS名字最帅,也可以显示一个特色logo,以表示自己操作系统的与众不同。)

编译生成 bootsect 可执行文件:

cd ./oslab/linux-0.11/boot/ // 进入当前目录 as86 -0 -a -o bootsect.o bootsect.s // 汇编语言源程序转换为可执行程序 ld86 -0 -s -o bootsect bootsect.o // 链接器将库函数绑定于可执行程序

使用 ls -l 命令查看 bootsect 的大小为 544KB,但引导程序的大小必须为一个磁盘扇区,即 512 KB。 原因是 ld86 产生的是 minix 格式的可执行文件,包含一个32KB的文件头,可使用如下指令删除文件头,生成 Image 文件。

dd bs=1 if=bootsect of=Image skip=32

复制 Image 文件到 linux-0.11 目录下,执行如下语句可成功启动,输出字符串 Loading system ...。

cd ./oslab // 回到oslab目录 ./run // 启动操作系统

阅读《Linux内核完全注释》,es:bp 寄存器保存需要显示字符串 msg1 起始位置,cx 保存字符串 msg1 的字符数。 此时,msg1 字符串包括长度为18的 Loading system ... 和3对换行符13和回车符10,长度共计24。如果需要输出长度为20的信息 SEANos is booting…,则修改ascii内容为 SEANos is booting… 和 cx 内容为26。 重新编译链接并 make 运行,输出结果如下:

2. setup.s 程序

改写setup.s主要完成如下功能:

bootsect.s能完成setup.s的载入,并跳转到setup.s开始地址执行。而setup.s向屏幕输出一行"Now we are in SETUP"。 ------------------------------------------------------------------ INT 0x10功能0x03 -------------------------------------------------------------- 描述: 在文本坐标下,读取光标各种信息 接受参数: AH 0x03 BH 显示页码 返回值: CH 光标的起始行 CL 光标的终止行 DH 行(Y 坐标) DL 列(X 坐标) -------------------------------------------------------------------

使用BIOS中断 int 0x10 功能号 0x03 获取光标:

mov ah,#0x03 !BIOS中断0x10功能号 ah=0x03 获取光标的位置 xor bh,bh ! bh寄存器清0,bh寄存器存储带获取光标的页号0 int 0x10 !执行 BIOS 0x10号中断 ------------------------------------------------------------------ INT 0x10功能0x13 -------------------------------------------------------------- 描述: 以电传打字机的方式显示字符串 接受参数: AH 0x13 AL 显示模式 BH 视频页 BL 属性值(如果AL=0x00或0x01) CX 字符串的长度 DH,DL 屏幕上显示起始位置的行、列值 ES:BP 字符串的段:偏移地址 返回值: 无 显示模式(AL): 0x00:字符串只包含字符码,显示之后不更新光标位置,属性值在BL中 0x01:字符串只包含字符码,显示之后更新光标位置,属性值在BL中 0x02:字符串包含字符码及属性值,显示之后不更新光标位置 0x03:字符串包含字符码及属性值,显示之后更新光标位置 -------------------------------------------------------------------

使用BIOS中断 int 0x10 功能号 0x13 显示字符串:

mov ax,#SETUPSEG ! 将段寄存器 es 设置为 setup.s 开始位置 mov es,ax mov bp,#MSG_SETUP !es:bp 寄存器保存显示的字符串的地址 mov cx,#25 ! 字符串长度 mov bx,#0x0007 ! 页号 0,光标属性为停在字符串结尾处 mov ax,#0x1301 !BIOS中断0x10功能号 ah=0x13 显示字符串 int 0x10 !执行 BIOS 0x10号中断

重新排列命令避免寄存器内容被覆盖,则 setup 添加输出字符串 MSG_SETUP 命令:

!print the message which wing edit mov ax,#SETUPSEG ! 将段寄存器 ds 设置为 setup.s 开始位置 mov es,ax mov ah,#0x03 !BIOS中断0x10功能号 ah=0x03 获取光标的位置 xor bh,bh ! bh寄存器清0,bh寄存器存储带获取光标的页号0 int 0x10 !执行 BIOS 0x10号中断 mov cx,#25 ! 字符串长度 mov bx,#0x0007 ! 页号 0,光标属性为停在字符串结尾处 mov bp,#MSG_SETUP !es:bp 寄存器保存显示的字符串的地址 mov ax,#0x1301 !BIOS中断0x10功能号 ah=0x13 显示字符串 int 0x10 !执行 BIOS 0x10号中断

末尾添加字符串 MSG_SETUP 内容:

MSG_SETUP: .byte 13,10 .ascii "Now we are in SETUP" .byte 13,10,13,10

同时需要清除89行~224行加载SYSTEM模块的内容,避免重复启动的现象出现,按照实验指导书编译执行,成功让 setup.s 向屏幕输出一行 "Now we are in SETUP"。

setup.s能获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存放在内存的特定地址,并输出到屏幕上。

光标位置信息保存至内存:

mov ah,#0x03 xor bh,bh int 0x10 ! 使用BIOS中断 int 0x10 功能号 0x03 获取光标 mov [0],dx ! 返回光标行列保存在寄存器dx,转存到0x90000

打印提示信息 Cursor Pos::

!print prompt of cursor position mov ah,#0x03 xor bh,bh int 0x10 mov cx,#13 mov bx,#0007 mov bp,#MSG_CURSOR mov ax,#0x1301 int 0x10 MSG_CURSOR: .byte 13,10 .ascii "Cursor POS:"

打印硬件参数:

------------------------------------------------------------------ INT 0x10功能0x0E -------------------------------------------------------------- 描述: 显示一个字符(光标前移) 接受参数: AH 0x0E AL 要显示字符的ASCII 返回值: 无 mov ax,#0xe0f int 0x10

硬件参数以二进制的形式保存在寄存器 dx,输出时十六进制形式需要每次循环左移4位,获取该左移4位(即1位十六进制数)求其ASCII码。求ASCII码时需要判断它是数字还是字母,数字则加 0X30 得对应ASCII,字母则加 0x37 得对应ASCII码,最后送显示器输出。下面以光标位置为例:

!print parameter of cursor position print_hex: mov cx,#4 ! 循环计数器 mov dx,[0] print_digit1: rol dx,#4 ! 循环左移4位 mov ax,#0xe0f ! BIOS中断0x10功能号 ah=0x0E 显示一个字符 and al,dl ! 获取dl的低4比特值 add al,#0x30 ! 数字加0x30 cmp al,#0x3a jl outp1 add al,#0x07 ! 字母多加0x37 outp1: int 0x10 ! 执行 BIOS 0x10号中断 loop print_digit1 ! loop指令:cx减1,然后判断cx是否等于0

打印回车换行:

!print cr and lf print_nl: mov ax,#0xe0d ! CR int 0x10 mov al,#0xa ! LF int 0x10

同理,可获取并打印扩展内存的大小。

------------------------------------------------------------------ INT 0x15功能0x88 -------------------------------------------------------------- 描述: 获取扩展内存的大小 接受参数: AH 0x88 返回值: AX 从绝对地址1MB开始的内存大小 ------------------------------------------------------------------- mov ah,#0x88 int 0x15 ! 调用0x15中断获取扩展内存大小 mov [2],ax ! 保存扩展内存大小至0x90002

完整 setup.s 源码:

! ! setup.s (C) 1991 Linus Torvalds ! ! setup.s is responsible for getting the system data from the BIOS, ! and putting them into the appropriate places in system memory. ! both setup.s and system has been loaded by the bootblock. ! ! This code asks the bios for memory/disk/other parameters, and ! puts them in a "safe" place: 0x90000-0x901FF, ie where the ! boot-block used to be. It is then up to the protected mode ! system to read them from there before the area is overwritten ! for buffer-blocks. ! ! NOTE! These had better be the same as in bootsect.s! INITSEG = 0x9000 ! we move boot here - out of the way SYSSEG = 0x1000 ! system loaded at 0x10000 (65536). SETUPSEG = 0x9020 ! this is the current segment .globl begtext, begdata, begbss, endtext, enddata, endbss .text begtext: .data begdata: .bss begbss: .text entry start start: ! print setup message mov ax,#SETUPSEG mov es,ax mov ah,#0x03 xor bh,bh int 0x10 mov cx,#25 mov bx,#0x0007 mov bp,#MSG_SETUP mov ax,#0x1301 int 0x10 ! ok, the read went well so we get current cursor position and save it for ! posterity. mov ax,#INITSEG ! this is done in bootsect already, but... mov ds,ax mov ah,#0x03 ! read cursor pos xor bh,bh int 0x10 ! save it in known place, con_init fetches mov [0],dx ! it from 0x90000. ! Get memory size (extended mem, kB) mov ah,#0x88 int 0x15 mov [2],ax ! Get video-card data: mov ah,#0x0f int 0x10 mov [4],bx ! bh = display page mov [6],ax ! al = video mode, ah = window width ! check for EGA/VGA and some config parameters mov ah,#0x12 mov bl,#0x10 int 0x10 mov [8],ax mov [10],bx mov [12],cx ! Get hd0 data mov ax,#0x0000 mov ds,ax lds si,[4*0x41] mov ax,#INITSEG mov es,ax mov di,#0x0080 mov cx,#0x10 rep movsb ! Get hd1 data mov ax,#0x0000 mov ds,ax lds si,[4*0x46] mov ax,#INITSEG mov es,ax mov di,#0x0090 mov cx,#0x10 rep movsb mov ax,#SETUPSEG mov ds,ax mov es,ax !print promopt of cursor position mov ah,#0x03 xor bh,bh int 0x10 mov cx,#13 mov bx,#0x0007 mov bp,#MSG_CURSOR mov ax,#0x1301 int 0x10 mov ax,#INITSEG mov ds,ax !print parameter of cursor position print_hex1: mov cx,#4 mov dx,[0] print_digit1: rol dx,#4 mov ax,#0xe0f and al,dl add al,#0x30 cmp al,#0x3a jl outp1 add al,#0x07 outp1: int 0x10 loop print_digit1 !print prompt of memory size mov ah,#0x03 xor bh,bh int 0x10 mov cx,#14 mov bx,#0x0007 mov bp,#MSG_MEMORY mov ax,#0x1301 int 0x10 mov ax,#INITSEG mov ds,ax !print parameter of memory size print_hex2: mov cx,#4 mov dx,[2] print_digit2: rol dx,#4 mov ax,#0xe0f and al,dl add al,#0x30 cmp al,#0x3a jl outp2 add al,#0x07 outp2: int 0x10 loop print_digit2 !print cr and lf print_nl: mov ax,#0xe0d ! CR int 0x10 mov al,#0xa ! LF int 0x10 MSG_SETUP: .byte 13,10 .ascii "Now we are in SETUP" .byte 13,10,13,10 MSG_CURSOR: .byte 13,10 .ascii "Cursor POS:" MSG_MEMORY: .byte 13,10 .ascii "Memory SIZE:" .text endtext: .data enddata: .bss endbss:

参考资料

北京大学《计算机组成》x86体系结构 清华大学《操作系统》BIOS、系统启动流程 《Linux内核完全注释》 哈工大李志军操作系统实验1 哈工大操作系统试验1 操作系统的引导

最新回复(0)