os从启动带内核加载
操作系统实验(真象还原)
第0章
操作系统是什么
操作系统把资源获取到后交给用户进程,而不允许用户直接访问硬件资源写一个操作系统需要了解硬件知识,这些硬件提供了软件接口,于是操作系统可以通过接口操作硬件
接口的标准
接口就是标准,访问外部硬件主要有两个方式:1. 将外设的内存映射到地址空间中,CPU通过地址总线访问该内存区域时就落到外设的内存中。如先显卡上的显存,就被映射到主机存储的0xB8000 ~ 0xBFFFF2. 通过访问I/O接口上的寄存器来实现对硬件的访问
应用程序对系统的访问
编译器提供了一套库函数,库函数中又封装了系统调用,这样的代码集合称之为运行库。而C语言的运行库叫C运行库,简称CRT(C Runtime Library)用户态和内核态是对于CPU来讲的,用户进程陷入内核态是指,由于内部或者外部中断的产生,开始执行一段内核的代码。当程序陷入内核后,它的上下文已经被保存到自己的0级特权栈中了,CPU中已经运行的内核程序。
分段
分段最大的好处就是实现了重定位,加载用户程序时,只用将这整个段的内容复制到新的位置,并将段基地址改为该地址。同时将段基地址*16 + 16位的段内偏移就可以访问20位的地址空间了。代码中的分段,在汇编级程序员可以人为将程序分为多个段。而在高级语言中,如C,gcc会把C语言写的程序分为代码段,数据段,栈段,.bss段,堆等部分。
地址
在实模式下,基地址+偏移,就是物理地址但是进入保护模式后,这个地址是线性地址,也叫选择字。它是个索引,通过这个索引可以在GDT中找到相应的段描述符,描述符中有段的起始,大小等信息,这样就得到了基地址,若没有开启分页功能,那么按照原来的方法这个就是物理地址。开启分页之后,这个地址就变成了虚拟地址,要转换为物理地址。
linux程序和windows程序
linux下的可执行文件是elf格式而windows下是PE格式,不能互通。同时linux下API和windows下不同
大端字节序和小端字节序
小端字节序:高字节在高地址,低字节在低地址
大端字节序:高字节在低地址,低字节在高地址小端优势: 低字节在低地址,在强制类型转换时就不用调节字节,低字节依旧在低字节
大端优势: 有符号数的符号就在低字节可以快速判断正负
BIOS中断,DOS中断,Linux中断
BIOS和DOS都运行在实模式下,由它们建立的中断调用都是在中断向量表中的(IVT)。它们都是通过软中断指令int来调用的。中断向量表中的每个向量的大小是4字节。4个字节描述了一个中断程序的的段基地址和段内偏移。因为中断向量表的大小为1024字节,所以最多容纳256个中断处理程序。计算机启动之初,中断向量表中的中断程序是由BIOS建立的,它从物理地址0开启添加各种历程。每个外设都有自己的内存,不过都是ROM。硬件自己的功能调用程序及初始化代码就存在ROM中,根据规范,第一个字节为0x55,第二个字节为0xAA,第三个字节是ROM中以512字节为单位的代码长度,第四个字节开始就是代码了内存的物理地址0xA0000到0xFFFFF中部分用于映射,这样BIOS就检测到了硬件的存在,并执行了硬件自带的历程和初始化函数。DOS也是运行在实模式下的,故其建立的中断调用也建立在中断向量表中,只不过中断向量号不能和BIOS的冲突。0x20~0x27是DOS中断。linux内核是进入保护模式后才建立例程的,此时存在的是中断描述符表(IDT)
Section和Segment
不管定义了多少section,最终会把属性相同的section放到一块,合并成一个大的segment
如何控制CPU获取下一条指令
在x86中每一指令的操作码的前缀都不会是另一个操作码,于是就可检测到是什么指令,并得到长度。
库函数是用户进程和内核的桥梁
头文件中一般仅仅只有函数声明,它一般说明了至少两件事:1. 函数返回值类型,参数的类型及其个数,用来确定分配栈空间2.该函数是外部函数,定义在其他的文件中,现在无法为其分配地址,需要在连接阶段将该函数体所在的目标文件一同连接时再安排地址。操作系统有自己支持,加载用户进程的规则,C运行时库就是针对该操作系统的规则,用于支持用户进程的代码库。用户进程要与C运行时库的诸多目标文件进行合并成一个可执行文件C运行时库,提供程序运行时所需要的库文件,而且还做了初始化的工作,所以即使不包含标准库文件,也会链接C运行时库用户进程需要操作系统的支持必须通过系统调用系统调用封装在库函数中
MBR,EBR,DBR,OBR
MBR是主引导记录,它位于磁盘的0盘0道1扇区。BIOS知道MBR在0盘0道1扇区,它会将0盘0道1扇区的MBR加载道物理地址0x7c00,然后跳过去执行。MBR 引导扇区中除了引导程序,还有64字节的分区表,里面是分区信息。表中每个分区表项占16字节,因此MBR分区表有4个分区,4个分区就是次引导程序的候选人,MBR程序遍历4个分区(0x55aa结尾),找到合适的人选将系统的控制权交给它。设置分区,如果互动标志0x80,说明这个分区中有操作系统,分区的起始扇区存放的是操作系统引导程序--内核加载器。MBR找活动分区后跳到OBR的起始处,OBR的前3字节是跳转指令,跳到引导程序,交出控制权。
第一章
环境的部署
安装CentOS 7下载 bochs 版本 2.6.*执行tar zxvf bochs-2.6.*.tar.gzcd 到bochs./configure \--prefix=/home/porterlu/bochs \--enable-debugger \--enable-disasm \--enable-iodebug \--enable-x86-debugger\--with-x \--with-x11make & make install配置bochs在bochs目录下建立.bochsrcmegs : 32 #设置内存romimage: file=/home/porterlu/bochs/share/bochs/BIOS-bochs-latestvgaromimage: file=/home/porterlu/bochs/share/bochs/VGABIOS-lgpl-latestboot: disklog: bochs.outmouse: enabled=0keyboard: keymap=/home/porterlu/bochs/share/bochs/keymaps/x11-pc-us.mapata0:enabled=1,ioaddr1=0x1f0,ioaddr2=0x3f0,irq=14ata0-master:type=disk,path="hd60M.img",mode=flat创建硬盘bin/bximage -hd -mode="flat" -size=60 -q hd60M.img
第二章
起始 | 结束 | 大小 | 用途 |
---|---|---|---|
FFFF0 | FFFFF | 16B | BISO的入口地址,jmp f000:e05b |
F0000 | FFFEF | 64KB-16B | BIOS去掉入口的部分 |
C8000 | EFFFF | 160KB | 用于映射硬件适配器的ROM或者内存映射式的I/O |
C0000 | C7FFF | 32KB | 显示适配器 |
B8000 | BFFFF | 32KB | 文本显示适配器 |
B0000 | B7FFF | 32KB | 黑白显示适配器 |
A0000 | AFFFF | 64KB | 彩色显示适配器 |
9FC00 | 9FFFF | 1KB | 扩展BIOS数据区 |
7E00 | 9FBFF | 约608KB | 可用区域 |
7C00 | 7DFF | 512B | MBR被BIOS加载到此处 |
500 | 7BFF | 30KB | 可用区域 |
400 | 4FF | 256B | BIOS数据区 |
000 | 3FF | 1KB | 中断向量表 |
;mbr.sSECTION MBR vstart=0x7c00mov ax,csmov ds,axmov es,axmov ss,axmov fs,axmov sp,0x7c00;调用int 6进行清屏
;AH = 0X06 ,AL=0 表示全部行,BH 是上卷行属性mov ax,0x600mov bx,0x700mov cx,0mov dx,0x18efint 0x10;获取光标mov ah,3mov bh,0int 0x10mov cx,5mov ax,0x1301 ;13号子功能,al=01显示字符串光标随着移动弄mov bx,0x2 ;bh=0,0号页,bl=2 黑底绿字int 0x10jmp $message db "1 MBR"times 510-($-$$) db 0db 0x55,0xaa
dd if=mbr.bin of=hd60M.img bs=512 count=1 conv=notrunc
第三章
寻址方式
直接寻址 立即数寻址 内存寻址 基址寻址(BX) 变址寻址(DI,SI)以BP作为基址的寻址:sp寄存器作为栈顶指针,相当于栈中的游标,专门给push和pop指令做为导航。既让sp不可随便移动,则用bp访问栈内的数据,bp默认的段寄存器就是ss,ss : bp 就可以把栈当成一个普通的数据段访问。执行一个function(),先将参数压入栈,调用这个函数,之后
push ebp ;
mov ebp,esp;
之后 将esp减去一个值,相当为函数申请了局部空间,而ebp就是局部空间的分界
实模式下的ret
call 和 ret 是一对配合,用于近调用和近返回;call far 和 retf是一对配合,用于远调用和远返回
实模式下的call
16位实模式相对近调用
近的意思就是在同一个段内,不用切换段,只用给出段内的偏移
相对的意思,只用给目标地址的相对地址就可以了
call proc_name
如果proc_name被编译器分配的地址是0x1234,call 指令操作数并不是0x1234,而是目标地址减去当前的pc值 = des - (src + length_of_ins)16位实模式间接绝对近调用call [addr]
这里call 默认使用的是近调用
这里的操作数也可以是访问寄存器16位实模式直接绝对远调用
call 新基址(立即数): 新偏移(立即数)
注意这里先将cs压入栈中后压ip16位实模式间接绝对远调用call far [bx]
这里操作数去内存中的4个字节,低字节是偏移,高字节是段基址
不支持寄存器寻找,只支持内存寻址
实模式下的jmp
16位实模式相对短转移 原理和近转移一致
jmp short 0x10
这是转移的范围缩小为 -128 ~ 12716位实模式相对近转移,
16位实模式间接绝对近转移,
16位实模式直接绝对远转移,
16位实模式间接绝对远转移 都和调用一致
标志寄存器flags
取8086的12位
第 0 位 是CF carry flag 用于检测最高位的进位和借位 用无符号数的加减法的溢出检测第 2 位 是PF parity flag 用于检测最低的8位中的 1 的个数是奇数还是偶数,如果是偶数个则置为1 否则 置为 0第 4 位 是AF auxiliary carry flag 是辅助进位标记 ,用于记录低 4 位的进位情况第 6 位 是ZF zero flag 用于表示计算结果是否为0第 7 位 是SF sign flag 是标记符号位,用于表示结果的正负,如果结果为负数则置为1第 8 位 是TF trap flag 是陷阱标志位,置为1 CPU就会就如单步运行模式第 9 位 是IF interrupt flag是中断标志位,置为0 会屏蔽可屏蔽中断第 10 位是DF direction flag是方向标志位,用于设置字符串操作中的地址增加方向,当置为1时,指令中的地址会自动减少一个单位第 11 位是OF overflow flag 用于检测有符号数是否有溢出现象的发生
显示器输出
mbr运行在实模式下,实模式下可以用BIOS的int 0x10中断打印字符串,因为中断向量表只在实模式下存在。显存是由显卡提供的,显卡可以读取这块内存,并显示到显示器上,显卡可以让显示器工作在图形模式也可以让显示器工作在文字模式这里用ascii 码表示字符显示地址分布
起始 | 结束 | 大小 | 用途 |
---|---|---|---|
C0000 | C7FFF | 32KB | 显示适配器BIOS |
B8000 | BFFFF | 32KB | 用于文本模式显示适配器 |
B0000 | B7FFF | 32KB | 用于黑白显示适配器 |
A0000 | AFFFF | 64KB | 用于彩色显示适配器 |
显卡的文本显示模式也分多种模式,用“行数 * 列数" 表示,显卡加电后默认是80* 25 ,也就是一屏可以显示2000个字符文本模式下也可以显示彩色字符,可以用连续的两个字符用于显示一个字符,高字节用于设置颜色属性,低字节用于显示字符。格式为 背景色 K R G B ,K用于设置是否闪烁前景色 I R G B ,I 用于设置是否高亮ascii 字符
bochs的调试
x,xp用于查看内存,,x后面接线性地址, xp后接物理地址
u 可以反汇编程序,后面跟的是线性地址
q 可以用于关闭虚拟机
set reg=val 可以用于设置寄存器的值
show指令,show mode可以CPU每次变换模式时。做出一个提示
show int 每次有中断就有提示
show call 每次调用函数就会提示c 会使程序一直执行下去
s 支持单步执行
p 每次也执行一条指令,但是遇到一个函数时会跳过整个函数vb,lb,pb 都是加一个断点,分别用的是虚拟地址,线性地址,物理地址
sb delta 再执行delta条指令就中断
sba time 从 CPU开始运行算起,执行time条执行就会中断watch命令
watch r physic_address 如果在物理地址physic_address 有读命令则产生中断
watch w physic_address 如果在物理地址physic_address 有写命令则产生中断
watch 会显示所有读写断点
unwatch 会清楚所有的读写断点blist 显示所有断点信息
bpd/bpe n(断点号) 可以禁用一个断点或者开启一个断点
d n(断点号) 可以删除一个断点setpmem physic_address size val 可以设置连续size个字节为val
r 可以显示寄存器信息
ptime 可以显示CPU启动后执行的指令数
print-stack n 打印栈内的信息info指令
info pb
info CPU
info fpu
info idt
info gdt
info ldt
info tss
info ivt
info flags/eflags
sreg 显示段寄存器
dreg 显示调试寄存器
creg 显示控制寄存器
info tab 显示页表中线性地址到物理地址的映射
page line_addr 将line_addr 映射到物理地址
硬盘
磁盘上通过盘面号,扇区号,磁道号定位一个512字节的扇区为了减少寻道时间,这里将地址的顺序记为磁道号,盘面号,扇区号。所以读取连续的空间,是优先变换磁头,即激活不同的磁头即可
磁盘接口的有的寄存器在进行读写操作时有不同的含义
这里数据寄存器是16位的,而其他的寄存器都是8位的
磁盘物理上使用 柱面,磁头,扇区定位的,即Cylinder,Head ,Sector (CHS)这种定位方式不适合给出一个地址,这里给出一种从0开始计数的方式即Logic Block Address(LBA)。
这里用的是LBA28,LBA寄存器有3个,分别是LBA low,LBA mid,LBA high 三个,存储0~23位。
而24~27放在Device寄存器的低4位,第4位用于指示使用主盘还是从盘,第六位用于设置是否开启LBA方式,其他位默认为10x1f7 读时是status,写时是command寄存器
主要有三个command
identify: 0xEC ,即硬盘识别.
read sector: 0x20 ,即读扇区。
write sector: 0x30 ,即写扇区 当是status寄存器时,第0位时err位,表示命令是否出错了,第三位时data request位表示磁盘是否把数据准备好了,第6位 DRDY磁盘就绪位,磁盘诊断时使用。第7位 BSY位表示是否磁盘是否忙。1. 选择通道,之后向该通道的sector count寄存器写入待操作的扇区数 2.向LBA寄存器写入低24位 3.向device寄存器写入LBA地址的24~27位,并将第6位为1,,并设置第4位选择主从磁盘 4.向command寄存器写入操作命令 5.读取status命令,判断磁盘工作死否完成 6.如果是读磁盘操作,将数据读出
-----boot.inc-------
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
-----mbr.s--------
%include "boot.inc"
SECTION MBR vstart = 0x7c00mov ax,csmov ds,axmov es,axmov ss,axmov fs,axmov sp,0x7c00mov ax,0b800mov gs,axmov ax,0x0600 ;表示6号功能,al=0表示全部行mov bx,0x0700 ;bh=07 设置上卷行的属性mov cx,0mov dx,0x184f ;表示右下角的坐标为第18行,第80列int 10h mov byte [gs:0x00],'1'mov byte [gs:0x01],0xa4mov byte [gs:0x00],' 'mov byte [gs:0x01],0xa4mov byte [gs:0x00],'M'mov byte [gs:0x01],0xa4mov byte [gs:0x00],'B'mov byte [gs:0x01],0xa4mov byte [gs:0x00],'R'mov byte [gs:0x01],0xa4mov eax,LOADER_START_SECTORmov bx,LOADER_BASE_ADDRmov cx,1call rd_disk_m_16jmp LOADER_BASE_ADDRrd_disk_m_16:mov esi,eaxmov di,cxmov dx,0x1f2; 输入sector countmov al,clout dx,almov eax,esimov dx,0x1f3out dx,almov cl,8shr eax,clmov dx,0x1f4out dx,alshr eax,clmov dx,0x1f5out dx,alshr eax,cland al,0x0for al,0xe0 ;主盘,开启LBAmov dx,1f6out dx,almov dx,0x1f7mov al,0x20out dx,al;读命令.not_ready:nopin al,dxand al,0x88 ;这里0x1f7 变为status寄存器cmp al,0x08 第7为0非忙,第3位为1表示已经就绪jnz .not_readymov ax,dimov dx,256mul dxmov cx,axmov dx,0x1f0.go_on_read:in ax,dxmov [bx],axadd bx,2loop .go_on_readret times 510-($-$$) db 0db 0x55,0xaa
第4章 保护模式
这里多了一个比例因子
运行模式反转
[bits 16],[bits 32]可以告诉编译器生成16位还是32位的指令
[bits 32]中使用16操作数便会在生成的机械码前缀加上0x66,那么CPU就知道是16位的操作数
寻址模式反转
如果[bits 32]中寻址用到了2字节操作数,0x67寻址方式反转
全局描述符表
全局描述符表是保护模式下内存段的登记表
总共32位段基址,20位段界限段的扩展方向可以向上也可以向下,数据段和代码段是向上,栈段是向下段界限边界 = (段界限 + 1)*(段界限粒度)-1第23位的G是0,表示粒度是1字节,G为1 表示粒度为4KB段信息会被缓冲到段描述符缓冲寄存器中,不用每次都进行读取并整合8~11位是type类型第12 位是表示是系统段 还是 数据段(软件的部分,无论代码还是数据),而系统段就是各种“门”的结构。第12位 S 加上 8~11位 描述类型
DPL表示特权级P表示段是否在内存中L是1 表示是64位代码,L是0表示是32位代码对于代码段这是D位,D为0 表示有效地址和操作数是16位,D为1 表示有效地址和操作数是32位对于栈段这是B位,B为0表示使用sp,B为1表示使用esp
全局描述表GDT,及选择子
全局体现在多个程序可以在里面定义自己的段描述符,是公共的
全局描述表需要有专门的寄存器指向它,这个寄存器便是GDTR
进入保护模式后寻址突破了1MB,可以重新加载GDT
而段寄存器cs,ds,es,fs,gs,ss中的便是段选择子,选择子中索引值加一些属性
TI表示是否全区描述符表
注意全局描述表的第0个是不可用的
打开A20
in al,0x92
or al,00000010b
out 0x92,al
保护模式的开关,CR0的PE位
mov eax,cr0
or eax,0x00000001
mov cr0,eax
进入保护模式代码
----boot.inc----LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2DESC_G_4K equ 0x800000 ;粒度为4KB
DESC_D_32 equ 0x400000 ;表示32位操作数
DESC_L equ 0x000000 ;表示是32位代码
DESC_AVL equ 0x000000 ;CPU不用此位,置为0
DESC_LIMIT_CODE2 equ 0xf0000; 段界限最高4位
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0x00000
DESC_P equ 0x8000 ;P置为1,表示在内存中
DESC_DPL_0 equ 0x0000
DESC_DPL_1 equ 0x2000
DESC_DPL_2 equ 0x4000
DESC_DPL_3 equ 0x6000DESC_S_CODE equ 0x1000 ;数据段
DESC_S_DATA equ DESC_S_CODE ;数据段
DESC_S_sys equ 0x0000 ;系统段
DESC_TYPE_CODE equ 0x800 ;只执行代码段
DESC_CODE_DATA equ 0x200 ;可写,向下扩展数据段DESC_CODE_HIGE4 equ 0x00<<24 + DESC_G_4K + DESC_D_32 + DESC_LIMIT_CODE2 + DESC_P + DESC+DPL_0 +DESC_S_CODE + DESC_TYPE_CODE + 0X00
;表示段基址高16位全是0,段界限粒度为4K,32位操作数,32位代码,段界限高4位为全1,同时在内存中,0级特权,非系统段,只执行代码段,DESC_DATA_HIGH4 equ 0x00 << 24 + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 +
DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00;段基地址高16位为0,段界限粒度为4K,32位操作数,32位代码,段界限高4位为全1,在内存中,0级特权级,非系统段,向上扩展可写数据段DESC_VIDEO_HIGH4 equ 0x00<<24 +DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00
;这里段界限高4位都是0RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b-----loader.s-----%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_startGDT_BASE: dd 0x00000000dd 0x00000000GODE_DESC: dd 0x0000FFFFdd DESC_CODE_HIGH4DATA_STACK_DESC: dd 0x0000FFFFdd DESC_DATA_HIGH4VIDEO_DESC: dd 0x80000007dd DESC_DATA_HIGH4GDT_SIZE equ $-GDT_BASEGDT-LIMIT equ GDT_SIZE -1times 60 dq 0SELECTOR_CODE equ (0x0001<<3) + TI_GDT +RPL0SELECTOR_DATA equ (0x0002<<3) +TI_GDT + RPL0SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT +RPL0gdt_ptr dw GDT_LIMITdd GDT_BASEloadermsg db '2 loader in real.'loader_start:mov sp,LOADER_BASE_ADDRmov bp,loadermsgmov cx,17mov ax,0x1301mov bx,0x001fmov dx,0x1800int 0x10in al,0x92or al,0x00000010bout 0x92,allgdt [gtr_ptr]mov eax,cr0or eax,0x00000001mov cr0,eaxjmp dword SELECTOR_CODE:p_mode_start[bits 32]
p_mode_start:mov ax,SELECTOR_DATAmov ds,axmov es,axmov ss,axmov esp,LOADER_STACK_TOPmov ax,SELECTOR_VIDEOmov gs,axmov byte [gs:160],'P'jmp $
保护模式段的保护
向段寄存器加载选择子时的保护
段基址 + 选择子索引*8 + 7 <=段描述符基址 + 段描述符界限
同时cs段只能加载具备可执行的段
具有可执行的段不允许加载到除了cs外的段寄存器
只有具有可写属性的段才能加载到DS,ES,FS,GS代码段和数据段的保护
段界限值 = (段界限 + 1)*(4K 或 1) -1
对于粒度为1 就是描述符中的段界限
对于粒度为4K ,就是界限*0x1000 + 0xFFF
要求EIP + 指令长度 - 1 <= 实际段界限大小栈段的保护
对于向下扩展的栈(地址减少),段界限 + 1=<栈的地址 <=栈基址
向内核迈进
获取物理内存容量
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR;此处省去GDT的定义
times 60 dq 0gdt_ptr dw GDT_LIMITdd GDT_BASE ards_buf times 244 db 0
ards_nr dw 0loader_start:xor ebx,ebxmov edx,0x534d4150mov di,ards_buf
.e820_mem_get_loop:mov eax,0x0000e820mov ecx,20int 0x15jc .e820_failed_so_try_e801add di,cxinc word [ards_nr]cmp ebx,0jnz .e820_mem_get_loopmov cx,[ards_nr]mov ebx,ards_bufxor edx,edx.find_max_mem_area:mov eax,[ebx]add eax,[ebx+8]add ebx,20cmp edx,eaxjge .next_ardsmov edx,eax
.next_ardsloop .find_max_mem_area-----------ax = e801---------.e820_failed_so_try_e801mov ax,e801int 0x15jc .e801_failed_so_try88mov cx,0x400mul cx ;以KB为单位shl edx,16and eax,0x0000FFFFor edx,eaxadd edx,0x11000000mov esi,edxxor eax,eaxmov ax,bxmov cx,0x10000mul ecxadd esi,eaxmov edx,esijmp .mem_get_ok.e801_failed_so_try88mov ah,0x88int 0x15jc .error_hltand eax,0x0000FFFFmov cx,0x400mul cxshl ex,16or edx,eaxadd edx,0x100000.mem_get_ok:mov [total_mem_bytes],edx
开启分页
P:表示是否在内存中
RW:读写位,表示是否可写
US:用户级,特权用户还是普通用户
PWT:写透
PCD:是否禁用cache缓存
A:Access
D: Dirty
PAT:略
G:全局页将一直在TLB
AVL:不管
setup_page:mov ecx,4096mov esi,0
;清零页目录
.clear_page_dir:mov byte[PAGE_DIR_TABLE_POS + esi],0inc esiloop .clear_page_dir;页目录基本结构建立.create_pdemov eax,PAGE_DIR_TABLE_POSadd eax,0x1000mov ebx,eaxor eax, PG_US_U| PG_RW_W|PG_Pmov [PAGE_DIR_TABLE_POS + 0x0],eaxmov [PAGE_DIR_TABLE_POS + 0xc00],eaxsub eax,0x1000mov [PAGE_DIR_TABLE_POS + 4092],eax;第一个页表的页表项mov ecx,256mov esi,0mov edx,PG_US_U|PG_RW_W|PG_P
.create_pte:mov [ebx+esi*4],edxadd edx,4096inc esiloop .create_ptemov eax, PAGE_DIR_TABLE_POSadd eax,2000or eax,PG_US_U|PG_RW_W|PG_Pmov ebx,PAGE_DIR_TABLE_POSmov ecx,254mov esi,769
.create_kernel_pde:mov [ebx+esi*4],eaxinc esiadd eax,0x1000loop .create_kernel_pderet
页表建立的代码
call setup_pagesgdt [gtr_ptr]mov ebx,[gdt_ptr + 2] ;获取基址
or dword [ebx + 0x18 +4],0xc0000000add dword [gdt_ptr + 2],0xc0000000add esp,0xc0000000mov eax,PAGE_DIR_TABLE_POS
mov cr3,eaxmov eax,cr0
or eax,0x8000000
mov cr0,eaxlgdt [gdt_ptr]mov byte [gs:160],'V'jmp $
虚拟地址 | 物理地址 |
---|---|
0x00000000~0x000fffff | 0x00000000~0x000fffff |
0xc0000000~0xc00fffff | 0x00000000~0x000fffff |
0xffc00000~0xffc00fff | 0x00101000~0x00101fff |
0xfff00000~0xfff00fff | 0x00101000~0x00101fff |
0xfffff000~0xffffffff | 0x00100000~00100fff |
ELF文件格式
e_ident[0] = x07f
e_ident[1~3]="ELF"
e_ident[4] 用于表示elf文件的类型,0表示无法识别的类型,1表示32位elf文件格式,2表示64位elf文件格式
e_ident[5] 指定大端字节序,还是小端字节序,还是非法编码格式
e_ident[6] 默认为1,表示当前版本
之后的位暂时不用e_type 占用2字节
ET_NONE 0 表示未知的目标文件格式
ET_REL 1 可重定位文件
ET_EXEC 2 可执行文件ET_DYN 3 动态共享目标文件ET_CODE 4 core文件,即程序崩溃时其内存映像的转储格式ET_LOPROC 0XFF00 特定处理器文件的扩展下边界ET_HIPROC 0XFFFF 特定处理器扩展文件上边界e_machine也占用两字节EM_NONE 0 未指定EM_M32 1 AT&T WE 32100EM_SPARC 2 SPARCEM_386 3 Intel 80386EM_68K 4 Motorola 68000EM_88K 5 Motorola 88000EM_860 7 Intel 80860EM_MIPS 8 MIPS RS3000e_version 占用4字节,表示版本e_entry 占用4字节,运行时告诉操作系统控制权交到的虚拟地址e_phoff 占用4字节,表示程序头表在文件内的字节偏移e_shoff 占用4字节,表示节头表在文件内的字节偏移e_flags 占用4字节,指明与处理器相关的参数e_phentsize 占用2字节,指明程序头表中的每个条目的字节大小e_phnum 占用2字节,指明程序头表中的条目数量e_shentsize 占用2字节,表述节头点表中每个条目字节大小e_shnum 占用2字节,表示节头点表中条目的数量e_shstrndx 指明string name table 在节头点表中索引的index
p_type 占用4字节
PT_NULL 0 忽略
PT_LOAD 1 可加载程序段
PT_DYNAMIC 2 动态链接信息
PT_INTERP 3 动态加载器的名称
PT_NOTE 4 一些辅助的附加信息
PT_SHLIB 5 保留
PT_PHDR 6 程序头表
PT_LOPROC 0X70000000
PT_HIPROC 0X7FFFFFFF 此范围内的类型预留给处理器专用p_offset 占用4字节 指明此段在文件内的起始偏移地址
p_vaddr 占用4字节 用来指明本段在内存中的起始虚拟地址
p_paddr 占用4字节 只用于和物理地址相关的系统中
p_filez 占用4字节 用来指明本段在文件中的大小
p_memsz 占用4字节 指明本段在内存中的大小
p_flags 占用4字节
PF_X 1 本段具有可执行的权限
PF_W 2 本段具有可写权限
PF_R 4 本段具有可读权限
PF_MASKOS 0x0ff00000 本段与操作系统相关
PF_MASKPROC 0Xf0000000 本段与处理器相关p_align 占用4字节 指明对齐方式
将内核载入内存
内核的文件是kernel.bin,这个文件由loader将其从硬盘读出,这里选择将kernel.bin写在磁盘的第9扇区,一共占用200个扇区内核加载到内存中,得有个加载地址,就是缓冲区。内核被加载到内存后,loader还要分析其elf文件格式将其扩展到新的位置,内核在内存中有两份拷贝,一份是elf格式的源文件,另一份是loader解析后在内存中生成的内存映像
----加载kernel----mov eax,KERNEL_START_SECTOR
mov ebx,KERNEL_BIN_BASE_ADDRmov ecx,200call rd_disk_m_32calll setup_page
movs[bdw]指令族,重复执行指令rep,方向指令cld和std,这些指令配合实现复制大块的数据movsb 用于从DS:ESI 搬运到 ES:EDI 一字节
cld,清楚方向标志位,用于让数据的源地址和目的地址逐渐扩大
ld kernel/main.o -Ttext 0xc0001500 -e main -o kernel/kernel.bin;-------------将kernel.bin的segment拷贝到elf中的地址-----------kernel_init:xor eax,eaxxor ebx,ebxxor ecx,ecxxor edx,edxmov dx,[KERNEL_BIN_BASE_ADDR + 42]mov ebx,[KERNEL_BIN_BASE_ADDR + 28]add ebx,KERNEL_BIN_BASE_ADDR;得到程序头表的起始地址mov cx,[KERNEL_BIN_BASE_ADDR + 44].each_segment:cmp byte [ebx+0],PT_NULLje .PTNULLpush dword[ebx+16];sizemov eax,[ebx+4]add eax,KERNEL_BIN_BASE_ADDRpush eax ;源地址push dword [ebx + 8];目的地址call mem_cpyadd esp,12.PTNULLadd ebx,edxloop .each_segmentretmem_cpy:cldpush ebpmov ebp,esppush ecxmov edi,[ebp+8]mov esi,[ebp+12]mov ecx,[ebp+16]rep movsbpop ecxpop ebpret
entry_kernel:call kernel_initmov esp,0xc009f00jmp KERNEL_ENTRY_POINT
特权级
特权级按照权力大小分为0,1,2,3级,数字越小权力越大,操作系统位于0级,系统程序位于1,2级,用户程序运行于第3级
TSS和特权级
TSS 即 Task State Segment,即任务状态段,是系统段的一种。
它是处理器默认的多任务解决方案使用的数据结构,它是每个任务都有的结构,默认104字节。任务在特权级变换时,涉及了栈的变换问题,不同的特权级涉及了不同的栈,在TSS中有三个分别是ss0和esp0,ss1和esp1,ss2和esp2。但是没有ss4,esp4在TSS中,当处理器由低特权级转移向高特权级时,处理器才会从TSS中找到对应的特权级栈的基址和指针,特权级4是最低特权级,没有低特权级会转向它。
而高特权级向低特权级转移,只有调用返回指令,那么低特权级指令已经在栈中,并不需要TSS。
显然TSS地址,也应该存在寄存器中表示当前任务,这就是TR寄存器。
CPL 和 DPL
CS寄存器的低两位即RPL(request privilege level)位就是CPL即处理器当前的特权级
DPL 中的D是描述符的意思,也就是段描述符,段描述符中的DPL位中记录着该值。
指令访问时会检查RPL,DPL,CPL
先不考虑RPL
对于访问的为代码段的情况:只能平级转移,即CPL和DPL相等,因为对于代码,低特权级能解决的,高特权级一定能解决
对于访问的是数据段的情况:只有CPL大于等于该数据段的DPL才能访问。非一致性代码就是上面将讲的代码段的情况,但是一致性代码要求CPL>=DPL就可以访问,且保持CPL不变,即与原来的特权级一致。
门,调用门,RPL序
除了任务们其余三种门都对应了一种例程,任务门可以在GDT,LDT,IDT中;调用门可以在GDT,LDT中;中断门和陷阱门只能在IDT中
任务们和调用门可以通过call和jmp调用。而陷阱门和中断门只存在与IDT中需要通过中断触发。
调用门可以通过call和jmp调用,call指令使用调用门可以实现向高特权级的转移,而jmp只能实现平级的代码转移
中断门,以int指令可以主动发中断的形式实现由低特权向高特权级的转移
陷阱门,一般编译器调试时使用
任务门,任务以TSS为单位,用来实现任务的切换,它可以用中断或者指令发起。当中断发生如果对应的中断向量号是任务们,则会发起任务切换首先,门描述符与数据描述符一样,只允许比自己特权级高的或者同等特权级才能访问,但是当前CPL不能比门描述符中的目标代码段的DPL高,否则转移就转移没什么意义。
门描述符用来指向某个内核例程,是个例程就需要参数,接下来讨论如何在3特权级向0特权级传递参数,调用时要将ss,esp保存。之后更新ss,esp到0级特权栈,将原来的ss和esp压入。之后压入参数(根据描述符中的参数个数字段得知参数的个数),0级特权栈复制3级特权栈中的参数信息。最后在0级特权栈压入CS,EIP,更新cs,EIP。注意当gs,es等寄存器中在返回时如果发现其中的特权级高于CPL,那么会自动置为0,触发异常。同时为了防止进入0级特权后,用户程序通过参数对内核数据进行修改,引入RPL的概念,RPL时请求特权级,在选择子的低两位。是指令中的参数的低两位,当执行内核程序时即使CPL = 0 ,但是RPL仍然会保持原来的3特权级,就无法访问内核数据对于用户伪造RPL的情况,操作系统可以通过arpl来用cs修改当前请求的选择子低两位,这里的cs在栈中,特权上升,将原本的cs,eip压入了栈。
IO特权级
在I/O读写的控制上,eflags中有IOPL位,只有特权级高于IOPL才能执行IO指令。如果特权级低于IOPL中的特权级,那么要看TSS中的IO位图是否开启了对应端口,io位图以0xff结尾
在IO位图满大小的情况,0xff可以防止访问超出界限的部分,因为1表示禁止。同时0xff可以提前结束IO位图,因为IO位图可以不满。
os从启动带内核加载相关推荐
- uboot加载linux内核加载那些内容,几个地址参数及uboot加载启动内核过程的理解
关于uBoot和Linux内核中几个地址参数及uboot加载启动内核过程的理解 uboot一般使用mkimage工具先制作一个启动映象文件来引导识别内核的,uboot源代码的tools/目录下有mki ...
- Linux内核启动及文件系统加载过程
当u-boot开始执行bootcmd命令,就进入linux内核启动阶段 与 u-boot 类似,普通 Linux 内核的启动过程也可以分为两个阶段,但针对压缩了的内核如 uImage 就要包括内核自解 ...
- Android中使用x5内核加载网页的实现
前言 联系方式 背景 SDK下载 SDK集成 使用 代码实现 前言 由于是使用的腾讯浏览服务,所以这里大部分介绍的是官网的一些东西,不过自己会做一些复杂使用部分的实现,不至于像官网上介绍的笼统. 联系 ...
- Windows内核加载器概念学习
最近看ReactOS源码分析相关,看到内核加载器概念相关的:原文如下: ReactOS源码分析--内核加载器(一) 计算机BIOS读取硬盘第一个扇区的数据到内存0x7C00位置,将控制权交给主引导记录 ...
- [feather]StarlingUi框架——初识feather、界面启动及Ui加载
这个星期六星期天,一是完结上周遗留下来的事情,也就是用feather来做一个应用:二是将这周的3D知识好好的记录和汇总一下. feather初识: 这是一个基于Starling的Ui框架集,Starl ...
- 腾讯X5 浏览器内核加载
1.腾讯X5浏览器 sdk 官网 地址 腾讯浏览服务 2.腾讯X5 浏览器 sdk 加载 需要内存卡权限 加载成功率才高,因为有了内存读取权限,可以读取腾讯QQ 和微信的X5 内核,共用他们的内核,不 ...
- 阵列卡服务器启动不到pe桌面,制作启动u盘加载阵列卡驱动无法安装怎么办
制作启动u盘加载阵列卡驱动无法安装怎么办?安装服务器时碰到没有阵列卡驱动无法安装的问题大家应该都碰到过.之前一次安装2003碰到没阵列卡驱动,解决的办法比较土,上网下了张别人做好的集成了raid驱动的 ...
- sofa启动的默认加载参数
sofa启动的默认加载参数 sofa-rpc-5.4.5\core\common\src\main\resources\com\alipay\sofa\rpc\common rpc-config-de ...
- [书]x86汇编语言:从实模式到保护模式 -- 第13章 mbr加载内核、内核加载应用程序
# mbr加载内核 1.0x7c00,16位实模式 2.进入保护模式前的准备工作:创建段描述符(代码段.数据段.堆栈段.显示缓冲区),构建gdt 3.进入保护模式 ; 开启保护模式 ; CR0的第1位 ...
最新文章
- 计算机网络谢希仁第七版课后答案完整版第四章 网络层
- linux c指定相对路径,linux c编程,选用popen()得到一个相对路径的绝对路径
- js你真的了解offsetWidth吗
- [EF4] CompiledQuery预编译性能提升 + 数据载入之大彻大悟
- 推荐系统里的那些坑儿
- python监视键盘_关于键盘监视的库pyHook与pythoncom
- [HDU 1015] Safecracker
- 请解释下Spring 框架中的IOC 容器?
- 11.17 模拟:总结
- html语言书写注意事项,HTML注意事项(学习笔记)
- 2016-2017-2 20155322 实验五 网络编程与安全
- fastadmin 后台view data-source关联报500错误问题
- canvas生成图片toDataURL报错的原因和解决方法
- 【王道计组笔记】定点数编码方式(原码,补码,反码)
- php利用ftp上传视频,PHP用FTP类上传文件视频等的简单实现方法
- 正确将博客网页保存为pdf
- Python实现阶跃函数、sigmoid函数、ReLU函数
- word文档图片画红线_word文档怎么画线条
- KS检验-如何理解KS检验中的p-value
- 介绍-Linux capability机制