配置环境
boch2.6.8
https://sourceforge.net/projects/bochs/files/bochs/2.6.8/bochs-2.6.8.tar.gz/download
chmod +x ./configure
sudo apt-get install libx11-dev xserver-xorg-dev xorg-dev
./configure \
--prefix=/home/robot/bochs \
--enable-debugger \
--enable-gdb-stub \
--enable-disasm \
--enable-iodebug \
--enable-x86-debugger \
--with-x \
--with-x11
make
make install
gcc4.4.7(ubuntu22.04)
sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu/ trusty main'
sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu/ trusty universe'
sudo apt update
sudo apt-get install lib32gcc1
sudo apt-get install g++-4.4
sudo apt-get install gcc-4.4 gcc-4.4-multilib
sudo apt-get install aptitude
sudo aptitude install gcc-4.4 gcc-4.4-multilib
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.4 40
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60
update-alternatives --config gcc
boch环境
disk
home\robot\bochs\bochsrc.disk
megs : 512
romimage: file=/home/robot/bochs/share/bochs/BIOS-bochs-latest
vgaromimage: file=/home/robot/bochs/share/bochs/VGABIOS-lgpl-latest
boot: disk
log: bochs.out
mouse:enabled=0
keyboard:keymap=/home/robot/bochs/share/bochs/keymaps/x11-pc-us.map
ata0:enabled=1,ioaddr1=0x1f0,ioaddr2=0x3f0,irq=14
#gdbstub:enabled=1,port=1234,text_base=0,data_base=0,bss_base=0
运行
bin/bochs
bin/bochs -f bochsrc.disk
导入配置文件
2
bochsrc.disk
创建启动盘
bin/bximage -hd -mode="flat" -size=60 -q hd60M.img
disk
home\robot\bochs\bochsrc.disk
#新加入的代码
ata0-master: type=disk, path="hd60M.img", mode=flat,cylinders=121,heads=16,spt=63
MBR
sudo apt-get install nasm
nasm -o ./boot/mbr.bin ./boot/mbr.S
dd if=/home/robot/bochs/boot/mbr.bin of=/home/robot/bochs/hd60M.img bs=512 count=1 conv=notrunc
home\robot\bochs\boot\mbr.S
;主引导程序
;-----------------------------------------
SECTION MBR vstart=0x7c00
;cs初始化其他寄存器
;reg (通用寄存器)
; 四个通用寄存器
; ax bx cx dx
; 四个专用寄存器
; SP(堆栈指针寄存器) 指向栈顶,操作系统使用
; BP(基指针寄存器) 用于访问栈中的数据(局部变量)
; SI(源变址寄存器) 用于字符串操作、地址计算
; DI(目的变址寄存器) 用于字符串操作(如 MOVS, LODS, STOS 指令)
; bx和si bx和di bp和si bp和di
;sreg (段寄存器)
; cs:代码段
; ds:数据段
; ss:栈段
; es:扩展段
; fs、gs:这两个寄存器用于提供额外的段支持,通常用于特殊的系统编程(如线程局部存储等)。
mov ax,cs
mov ds,ax
mov ss,ax
mov es,ax
mov fs,ax
mov sp,0x7c00
;-----------------------------------------
;清屏
mov ah,0x06
mov al,0x00
mov bh,0x00
mov cx,0x0000
mov dx,0x184f
int 10h
;-----------------------------------------
;获取光标位置
mov ah,0x03
mov bh,0x00
int 10h
;-----------------------------------------
;打印字符串
mov ax,message
mov bp,ax
mov ah,0x13
mov al,0x01
mov bh,0x00
mov bl,0x02
mov cx,0x0005
int 10h
message db "1 MBR"
jmp $
;-----------------------------------------
;标志MBR
times 510-($-$$) db 0
db 0x55,0xaa
home\robot\bochs\tool\xxd.sh
#usage: sh xxd.sh 文件起始地址长度
xxd -u -a -g 1 -s $2 -l $3 $1
#-u use upper case hex letters. Default is lower case.
#
#-a | -autoskIP
# toggle autoskIP: A single '*' replaces nul-lines. Default off.
#
#-g bytes | -groupsize bytes
# separate the output of every <bytes> bytes (two hex characters or eight bit-digits each) by a whitespace. Specify -g 0 to
# suppress grouping. <Bytes> defaults to 2 in normal mode and 1 in bits mode. Grouping does not apply to postscrIPt or
# include style.
#
#-c cols | -cols cols
# format <cols> octets per line. Default 16 (-i: 12, -ps: 30, -b: 6). Max 256.
#
#-s [+][-]seek
# start at <seek> bytes abs. (or rel.) infile offset. + indicates that the seek is relative to the current stdin file position
# (meaningless when not reading from stdin). - indicates that the seek should be that many characters from the end of
# the input (or if combined with +: before the current stdin file position).
# Without -s option, xxd starts at the current file position.
sh ./tool/xxd.sh ./boot/mbr.bin 0 512
完善
cd boot
nasm -o mbr.bin mbr.S
nasm -o loader.bin loader.S
cd ..
dd if=/home/robot/bochs/boot/mbr.bin of=/home/robot/bochs/hd60M.img bs=512 count=1 conv=notrunc
dd if=/home/robot/bochs/boot/loader.bin of=/home/robot/bochs/hd60M.img bs=512 count=2 seek=2 conv=notrunc
bin/bochs -f bochsrc.disk
home\robot\bochs\boot\mbr.S
;主引导程序
;-----------------------------------------
%include "boot.inc"
SECTION MBR vstart=0x7c00
;cs初始化其他寄存器
;reg (通用寄存器)
; 四个通用寄存器
; ax bx cx dx
; 四个专用寄存器
; SP(堆栈指针寄存器) 指向栈顶,操作系统使用
; BP(基指针寄存器) 用于访问栈中的数据(局部变量)
; SI(源变址寄存器) 用于字符串操作、地址计算
; DI(目的变址寄存器) 用于字符串操作(如 MOVS, LODS, STOS 指令)
; bx和si bx和di bp和si bp和di
;sreg (段寄存器)
; cs:代码段
; ds:数据段
; ss:栈段
; es:扩展段
; fs、gs:这两个寄存器用于提供额外的段支持,通常用于特殊的系统编程(如线程局部存储等)。
mov ax,cs
mov ds,ax
mov ss,ax
mov es,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
;-----------------------------------------
;清屏
mov ah,0x06
mov al,0x00
mov bh,0x00
mov cx,0x0000
mov dx,0x184f
int 10h
;-----------------------------------------
;打印字符串
mov byte [gs:0x00],' '
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],'M'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'B'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'R'
mov byte [gs:0x07],0xA4
mov eax,LOADER_START_SECTOR ;规定的就是dx里面存放的是端口号 ax是需要或者输送的信息
mov bx,LOADER_BASE_ADDR ;把要目标内存位置放进去 bx常作地址储存
mov cx,4 ;读取磁盘数 cx常作计数
call rd_disk_m_16
jmp LOADER_BASE_ADDR
;-----------------------------------------
;读取硬盘
rd_disk_m_16:
; 备份
mov esi,eax
mov di,cx
; 端口设置扇区数
mov dx,0x1f2 ;OUT 指令的端口号存储在dx寄存器中
mov al,cl ;OUT 指令的源操作数必须是累加器
out dx,al
mov eax,esi
; 往该通道上的三个 LBA 寄存器写入扇区起始地址的低 24 位
mov cl,0x08 ;如果移位位数大于1,必须将移位位数放在cl中
mov dx,0x1f3
out dx,al
shr eax,cl
mov dx,0x1f4
out dx,al
shr eax,cl
mov dx,0x1f5
out dx,al
shr eax,cl
; 往 device 寄存器中写入 LBA 地址的 24~27 位,并置高四位为0x1110
and al,0x0f
or al,0xe0
mov dx,0x1f6
out dx,al
; 往该通道上的 command 寄存器写入操作命令 0x20读命令
mov al,0x20
mov dx,0x1f7
out dx,al
; 读取该通道上的 status 寄存器,判断硬盘工作是否完成。第四位准备,第七位忙碌
.not_ready:
nop
in al,dx
and al,0x88
cmp al,0x08
jnz .not_ready
; 读出数据
; 每个扇区512个字节,256个字
mov ax,di
mov dx,256
mul dx
mov cx,ax
mov dx,0x1f0
.go_on_read:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on_read
ret
;-----------------------------------------
;标志MBR
times 510-($-$$) db 0
db 0x55,0xaa
home\robot\bochs\boot\loader.S
%include "boot.inc"
SECTION MBR vstart=LOADER_BASE_ADDR
mov ax,0xb80a
mov gs,ax
mov byte [gs:0x00],0x00
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],'L'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'O'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'D'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'E'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0A],'R'
mov byte [gs:0x0B],0xA4
jmp $
保护模式
cd boot
make burn
cd ..
bin/bochs -f bochsrc.disk
home\robot\bochs\boot\makefile
.PHONY:build burn clean
mbr_source = mbr.S
mbr_target = mbr.bin
loader_source=loader.S
loader_target=loader.bin
hard_disk=../hd60M.img
build:
nasm $(mbr_source) -o $(mbr_target)
nasm $(loader_source) -o $(loader_target)
burn:
@make build
dd if=$(mbr_target) of=$(hard_disk) bs=512 count=1 conv=notrunc
dd if=$(loader_target) of=$(hard_disk) bs=512 count=2 seek=2 conv=notrunc
home\robot\bochs\boot\boot.inc
;-------------loader----------------
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
;-------------------- gdt描述符属性 --------------------------------
DESC_G_4K equ 1_00000000000000000000000b ;第23位G 表示4K或者1MB位 段界限的单位值 此时为1则为4k
DESC_D_32 equ 1_0000000000000000000000b ;第22位D/B位 表示地址值用32位EIP寄存器 操作数与指令码32位
DESC_L equ 0_000000000000000000000b ;第21位 设置成0表示不设置成64位代码段 忽略
DESC_AVL equ 0_00000000000000000000b ;第20位 是软件可用的 操作系统额外提供的 可不设置
DESC_LIMIT_CODE2 equ 1111_0000000000000000b ;第16-19位 段界限的最后四位 全部初始化为1 因为最大段界限*粒度必须等于0xffffffff
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ;相同的值 数据段与代码段段界限相同
DESC_LIMIT_VIDEO2 equ 0000_0000000000000000b ;第16-19位 显存区描述符VIDEO2 书上后面的0少打了一位 这里的全是0为高位 低位即可表示段基址
DESC_P equ 1_000000000000000b ;第15位 P present判断段是否存在于内存
DESC_DPL_0 equ 00_0000000000000b ;第13-14位 这两位更是重量级 Privilege Level 0-3
DESC_DPL_1 equ 01_0000000000000b ;0为操作系统 权力最高 3为用户段 用于保护
DESC_DPL_2 equ 10_0000000000000b
DESC_DPL_3 equ 11_0000000000000b
DESC_S_sys equ 0_000000000000b ;第12位为0 则表示系统段 为1则表示数据段
DESC_S_CODE equ 1_000000000000b ;第12位与type字段结合 判断是否为系统段还是数据段
DESC_S_DATA equ DESC_S_CODE
DESC_TYPE_CODE equ 1000_00000000b ;第9-11位表示该段状态 1000 可执行 不允许可读 已访问位0
;x=1 c=0 r=0 a=0
DESC_TYPE_DATA equ 0010_00000000b ;第9-11位表示该段状态 0010 可写
;x=0 e=0 w=1 a=0
;代码段描述符高位4字节初始化 (0x00共8位 <<24 共32位初始化0)
;4KB为单位 Data段32位操作数 初始化的部分段界限 最高权限操作系统代码段 P存在表示 状态
DESC_CODE_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0X00
;数据段描述符高位4字节初始化
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
;显存段描述符高位4字节初始ua
DESC_VIDEO_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0X0B
;-------------------- 选择子属性 --------------------------------
;第0-1位 RPL 特权级比较是否允许访问 第2位TI 0表示GDT 1表示LDT 第3-15位索引值
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b
home\robot\bochs\boot\loader.S
;引导程序
;-----------------------------------------
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
;-----------------------------------------
;GDT描述符
;GDT第一个为空描述符,双字(32位)对齐
GDT_BASE:
dd 0x00000000
dd 0x00000000
;代码段描述符
CODE_DESC:
dd 0x0000FFFF
dd DESC_CODE_HIGH4
;数据和堆栈段描述符
DATA_STACK_DESC:
dd 0x0000FFFF
dd DESC_DATA_HIGH4
;显存段描述符
;0xBFFFF - 0xB8000 +1 = 0x8000(32KB),32KB / 4KB-1 = 7
VIDEO_DESC:
dd 0x80000007
dd DESC_VIDEO_HIGH4
;GDT的大小和界限
GDT_SIZE equ $-GDT_BASE
GDT_LIMIT equ GDT_SIZE-1
;预留60个段描述符(每个8字节)
times 60 dq 0
total_mem_bytes dd 0
;-----------------------------------------
;定义选择子(代码段、数据段、显存段)
SELECTOR_CODE equ (0x0001<<3)+TI_GDT+RPL0
SELECTOR_DATA equ (0x0002<<3)+TI_GDT+RPL0
SELECTOR_VIDEO equ (0x0003<<3)+TI_GDT+RPL0
;GDT指针
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
;-----------------------------------------
;loader设置
loader_start:
; 初始化寄存器
mov sp,LOADER_BASE_ADDR
mov ax,0
mov ax,0xb80a
mov gs,ax
; 打开A20地址线
; 将端口 0x92 的第 1 位置 1
in al,0x92
or al,0000_0010B
out 0x92,al
; 加载GDT到gdtr寄存器
lgdt [gdt_ptr]
; 控制寄存器cr0的pe位置1,表示保护模式下运行
mov eax,cr0
or eax,0x0000_0001
mov cr0,eax
; 打印
mov byte [gs:0x00],0x00
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],'L'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'O'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'D'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'E'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0A],'R'
mov byte [gs:0x0B],0xA4
; 远跳转,为避免流水线和分支预测提前预取16位指令,刷新流水线
jmp SELECTOR_CODE:p_mode_start
;-----------------------------------------
;已经打开保护模式
[bits 32]
p_mode_start:
; 将各段寄存器(数据段、附加段、堆栈段)初始化为数据段选择子
; 设置栈顶
mov ax,SELECTOR_DATA
mov ds,ax
mov es,ax
mov ss,ax
mov esp,LOADER_STACK_TOP
; 将显存段寄存器初始化为显存段选择子
mov ax,SELECTOR_VIDEO
mov gs,ax
jmp $
载入内核
查看内存容量
cd boot
make burn
cd ..
bin/bochs -f bochsrc.disk
xp 0xb00
home\robot\bochs\boot\loader.S
;引导程序
;-----------------------------------------
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
;-----------------------------------------
;GDT描述符
;GDT第一个为空描述符,双字(32位)对齐
GDT_BASE:
dd 0x00000000
dd 0x00000000
;代码段描述符
CODE_DESC:
dd 0x0000FFFF
dd DESC_CODE_HIGH4
;数据和堆栈段描述符
DATA_STACK_DESC:
dd 0x0000FFFF
dd DESC_DATA_HIGH4
;显存段描述符
;0xBFFFF - 0xB8000 +1 = 0x8000(32KB),32KB / 4KB-1 = 7
VIDEO_DESC:
dd 0x80000007
dd DESC_VIDEO_HIGH4
;GDT的大小和界限
GDT_SIZE equ $-GDT_BASE
GDT_LIMIT equ GDT_SIZE-1
;预留59个 define double四字型 8字描述符
times 59 dq 0
times 5 db 0 ;为了凑整数 0x800 导致前面少了三个字节
;地址0xb00
total_mem_bytes dd 0
;ards结构体及其数量
ards_buf times 244 db 0
ards_nr dw 0
;-----------------------------------------
;定义选择子(代码段、数据段、显存段)
SELECTOR_CODE equ (0x0001<<3)+TI_GDT+RPL0
SELECTOR_DATA equ (0x0002<<3)+TI_GDT+RPL0
SELECTOR_VIDEO equ (0x0003<<3)+TI_GDT+RPL0
;GDT指针
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
;-----------------------------------------
loader_start:
;检测内存容量
;BIOS中断0x15三个字子功能
;1、EAX=0xE820:遍历主机上全部内存
; 循环获取内存信息内容置于ards结构体中
mov ax,0
mov es,ax
mov di,ards_buf
mov edx,0x534d4150 ;固定数值,签名标记
xor ebx,ebx
; 循环记录ards,返回值被覆盖的循环初始化(while结构)
.e820_mem_get_loop:
mov eax,0x0000e820
mov ecx,0x00000014
int 15h
jc .e801_mem_get_loop
add di,cx ;使指针指向下一个ards结构体
inc word [ards_nr] ;inc使ards_nr对应的内存加一,在这里循环统计ards的数量
cmp ebx,0
jnz .e820_mem_get_loop
; 找出所有ards中内存容量最大的那一个,即为要获取的内存容量(冒泡排序)
; 初始化
mov cx,[ards_nr]
mov ebx,ards_buf
xor edx,edx
; 循环(do while结构)
.e820_find_max_mem_area:
; BaseAddrLow+LengthLow 是一片内存区域的上限,单位为字节
mov eax,[ebx]
add eax,[ebx+8]
cmp edx,eax ;if结构用于比较edx和eax的大小
jge .next_ards ;SF(非负为1)=OF(非0为1)
mov edx,eax
.next_ards:
add ebx,20 ;指向下一个ards
loop .e820_find_max_mem_area
jmp .mem_get_ok
;2、AX=0xE801: 分别检测低 15MB 和 16MB~4GB 的内存,最大支持 4GB。
.e801_mem_get_loop:
mov ax,0xe801
int 15h
jc .ah88_mem_get_loop
; 16位以字节为单位最多表示64KB范围的内存,32位以字节为单位最多表示4GB范围的内存
; AX中为0~15MB的内存容量,单位为1KB,可将其分为0~64KB和64KB~15MB。有1MB是缓冲区,
; BX中为16MB~4GB的内存容量,单位为64KB
; 先算出AX中的内存容量,并转换成以字节为单位
and eax,0x0000ffff
mov cx,0x400
; 积为32位,低16位在ax中,高16位在dx中
mul cx
shl edx,16
or edx,eax
add edx,0x100000
mov esi,edx ;乘法覆盖了edx,因此要备份
; 再算出BX中的内存容量,并转换成以字节为单位
xor eax,eax
mov ax,bx
mov ecx,0x10000
; 积为64位,低32位在eax中,高32位在edx中
mul ecx
mov edx,esi
add edx,eax
jmp .mem_get_ok
;3、AH=0x88: 最多检测出 64MB 内存,实际内存超过此容量也按照 64MB 返回。
.ah88_mem_get_loop:
mov ah,0x88
int 15h
jc .mem_get_error
and eax,0x0000ffff
mov cx,0x400
mul cx
shl edx,16
or edx,eax
add edx,0x100000
jmp .mem_get_ok
.mem_get_error:
jmp $
.mem_get_ok:
mov [total_mem_bytes],edx
;-----------------------------------------
;打开保护模式
; 初始化寄存器
mov sp,LOADER_BASE_ADDR
mov ax,0xb80a
mov gs,ax
mov ax,0
; 打开A20地址线
; 将端口 0x92 的第 1 位置 1
in al,0x92
or al,0000_0010B
out 0x92,al
; 加载GDT到gdtr寄存器
lgdt [gdt_ptr]
; 控制寄存器cr0的pe位置1,表示保护模式下运行
mov eax,cr0
or eax,0x0000_0001
mov cr0,eax
; 打印
mov byte [gs:0x00],0x00
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],'L'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'O'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'D'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'E'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0A],'R'
mov byte [gs:0x0B],0xA4
; 远跳转,为避免流水线和分支预测提前预取16位指令,刷新流水线
jmp SELECTOR_CODE:p_mode_start
;-----------------------------------------
;已经打开保护模式
[bits 32]
p_mode_start:
; 将各段寄存器(数据段、附加段、堆栈段)初始化为数据段选择子
; 设置栈顶
mov ax,SELECTOR_DATA
mov ds,ax
mov es,ax
mov ss,ax
mov esp,LOADER_STACK_TOP
; 将显存段寄存器初始化为显存段选择子
mov ax,SELECTOR_VIDEO
mov gs,ax
mov byte [gs:160],'P'
mov byte [gs:161],0xA4
jmp $
home\robot\bochs\boot\loader.S
.PHONY:build burn clean
mbr_source = mbr.S
mbr_target = mbr.bin
loader_source=loader.S
loader_target=loader.bin
hard_disk=../hd60M.img
build:
nasm $(mbr_source) -o $(mbr_target)
nasm $(loader_source) -o $(loader_target)
burn:
@make build
dd if=$(mbr_target) of=$(hard_disk) bs=512 count=1 conv=notrunc
dd if=$(loader_target) of=$(hard_disk) bs=512 count=3 seek=2 conv=notrunc
内存分页
home\robot\bochs\boot\loader.S
;引导程序
;-----------------------------------------
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
;-----------------------------------------
;GDT描述符
;GDT第一个为空描述符,双字(32位)对齐
GDT_BASE:
dd 0x00000000
dd 0x00000000
;代码段描述符
CODE_DESC:
dd 0x0000FFFF
dd DESC_CODE_HIGH4
;数据和堆栈段描述符
DATA_STACK_DESC:
dd 0x0000FFFF
dd DESC_DATA_HIGH4
;显存段描述符
;0xBFFFF - 0xB8000 +1 = 0x8000(32KB),32KB / 4KB-1 = 7
VIDEO_DESC:
dd 0x80000007
dd DESC_VIDEO_HIGH4
;GDT的大小和界限
GDT_SIZE equ $-GDT_BASE
GDT_LIMIT equ GDT_SIZE-1
;预留59个 define double四字型 8字描述符
times 59 dq 0
times 5 db 0 ;为了凑整数 0x800 导致前面少了三个字节
;地址0xb00
total_mem_bytes dd 0
;ards结构体及其数量
ards_buf times 244 db 0
ards_nr dw 0
;-----------------------------------------
;定义选择子(代码段、数据段、显存段)
SELECTOR_CODE equ (0x0001<<3)+TI_GDT+RPL0
SELECTOR_DATA equ (0x0002<<3)+TI_GDT+RPL0
SELECTOR_VIDEO equ (0x0003<<3)+TI_GDT+RPL0
;GDT指针
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
;-----------------------------------------
loader_start:
;检测内存容量
;BIOS中断0x15三个字子功能
;1、EAX=0xE820:遍历主机上全部内存
; 循环获取内存信息内容置于ards结构体中
mov ax,0
mov es,ax
mov di,ards_buf
mov edx,0x534d4150 ;固定数值,签名标记
xor ebx,ebx
; 循环记录ards,返回值被覆盖的循环初始化(while结构)
.e820_mem_get_loop:
mov eax,0x0000e820
mov ecx,0x00000014
int 15h
jc .e801_mem_get_loop
add di,cx ;使指针指向下一个ards结构体
inc word [ards_nr] ;inc使ards_nr对应的内存加一,在这里循环统计ards的数量
cmp ebx,0
jnz .e820_mem_get_loop
; 找出所有ards中内存容量最大的那一个,即为要获取的内存容量(冒泡排序)
; 初始化
mov cx,[ards_nr]
mov ebx,ards_buf
xor edx,edx
; 循环(do while结构)
.e820_find_max_mem_area:
; BaseAddrLow+LengthLow 是一片内存区域的上限,单位为字节
mov eax,[ebx]
add eax,[ebx+8]
cmp edx,eax ;if结构用于比较edx和eax的大小
jge .next_ards ;SF(非负为1)=OF(非0为1)
mov edx,eax
.next_ards:
add ebx,20 ;指向下一个ards
loop .e820_find_max_mem_area
jmp .mem_get_ok
;2、AX=0xE801: 分别检测低 15MB 和 16MB~4GB 的内存,最大支持 4GB。
.e801_mem_get_loop:
mov ax,0xe801
int 15h
jc .ah88_mem_get_loop
; 16位以字节为单位最多表示64KB范围的内存,32位以字节为单位最多表示4GB范围的内存
; AX中为0~15MB的内存容量,单位为1KB,可将其分为0~64KB和64KB~15MB。有1MB是缓冲区,
; BX中为16MB~4GB的内存容量,单位为64KB
; 先算出AX中的内存容量,并转换成以字节为单位
and eax,0x0000ffff
mov cx,0x400
; 积为32位,低16位在ax中,高16位在dx中
mul cx
shl edx,16
or edx,eax
add edx,0x100000
mov esi,edx ;乘法覆盖了edx,因此要备份
; 再算出BX中的内存容量,并转换成以字节为单位
xor eax,eax
mov ax,bx
mov ecx,0x10000
; 积为64位,低32位在eax中,高32位在edx中
mul ecx
mov edx,esi
add edx,eax
jmp .mem_get_ok
;3、AH=0x88: 最多检测出 64MB 内存,实际内存超过此容量也按照 64MB 返回。
.ah88_mem_get_loop:
mov ah,0x88
int 15h
jc .mem_get_error
and eax,0x0000ffff
mov cx,0x400
mul cx
shl edx,16
or edx,eax
add edx,0x100000
jmp .mem_get_ok
.mem_get_error:
jmp $
.mem_get_ok:
mov [total_mem_bytes],edx
;-----------------------------------------
;打开保护模式
; 初始化寄存器
mov sp,LOADER_BASE_ADDR
mov ax,0xb80a
mov gs,ax
mov ax,0
; 打开A20地址线
; 将端口 0x92 的第 1 位置 1
in al,0x92
or al,0000_0010B
out 0x92,al
; 加载GDT到gdtr寄存器
lgdt [gdt_ptr]
; 控制寄存器cr0的pe位置1,表示保护模式下运行
mov eax,cr0
or eax,0x0000_0001
mov cr0,eax
; 打印
mov byte [gs:0x00],0x00
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],'L'
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'O'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'D'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'E'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0A],'R'
mov byte [gs:0x0B],0xA4
; 远跳转,为避免流水线和分支预测提前预取16位指令,刷新流水线
jmp SELECTOR_CODE:p_mode_start
;-----------------------------------------
;已经打开保护模式
[bits 32]
p_mode_start:
; 将各段寄存器(数据段、附加段、堆栈段)初始化为数据段选择子
; 设置栈顶
mov ax,SELECTOR_DATA
mov ds,ax
mov es,ax
mov ss,ax
mov esp,LOADER_STACK_TOP
; 将显存段寄存器初始化为显存段选择子
mov ax,SELECTOR_VIDEO
mov gs,ax
mov byte [gs:160],'P'
mov byte [gs:161],0xA4
;-----------------------------------------
;启动分页
call setup_page
sgdt [gdt_ptr] ;要将描述符表地址及偏移量写入内存gdt_ptr,一会儿用新地址重新加载
mov ebx, [gdt_ptr + 2] ;将gdt描述符中视频段描述符中的段基址+0xc0000000
or dword [ebx + 0x18 + 4], 0xc0000000 ; 视频段是第 3 个段描述符,每个描述符是8字节,故0x18
add dword [gdt_ptr + 2], 0xc0000000 ; 将gdt的基址加上0xc0000000使其成为内核所在的高地址
add esp, 0xc0000000 ; 将栈指针同样映射到内核地址
;将页表地址写入控制寄存器cr3
mov eax,PAGE_DIR_TABLE_POS
mov cr3,eax
;寄存器cr0的PG位(31位)置1
mov eax,cr0
or eax,0x80000000
mov cr0,eax
lgdt [gdt_ptr] ;在开启分页后,用gdt新的地址重新加载
mov ax,SELECTOR_VIDEO
add ax,0xA
mov gs,ax
mov byte [gs:160],'V'
mov byte [gs:161],0xA4
jmp $
;-----------------------------------------
;分页创建函数
setup_page:
; 把页目录对应的空间清空
mov ecx,0x1000
mov esi,0
.clear_page_dir:
mov byte [PAGE_DIR_TABLE_POS+esi],0
inc esi
loop .clear_page_dir
; 创建页目录项pde
.create_pde:
mov eax,PAGE_DIR_TABLE_POS
add eax,0x1000 ;第一个页表的位置及属性
mov ebx,eax ;备份做基址
or eax,PG_P|PG_RW_W|PG_US_U ;用户级,可读写,现在eax高20位为页表物理地址,除了最低3位以外的控制状态位为0
; 将0~0xc00的空间给用户空间,而将0xc00之上的空间给内核
; 0xc00 表示第768(0xc00/4)个页表占用的目录项,一个页表4K大小0x10_0000
; 0x0~0xbfff_ffff共计3G属于用户进程;0xc000_0000~0xffff_ffff共计1G属于内核
mov [PAGE_DIR_TABLE_POS+0x0],eax
mov [PAGE_DIR_TABLE_POS+0xc00],eax
sub eax,0x1000 ;指向页目录表自身
mov [PAGE_DIR_TABLE_POS+0x1000],eax ;虚拟内存最后一个目录项 指向页目录表自身 为了动态操纵页表
; 创建页表pte
; 循环256次,每次初始化一个页表项
mov ecx,0x100
mov esi,0
mov edx,PG_P|PG_RW_W|PG_US_U ;用户级,可读写,高位地址全0
.create_pte:
mov [ebx+esi*4],edx
add edx,0x1000
inc esi
loop .create_pte
; 创建内核其他页表的pde
; 0xc01~0xcfe
mov eax,PAGE_DIR_TABLE_POS
mov ebx,eax
add eax,0x2000
or eax,PG_P|PG_RW_W|PG_US_U
mov ecx,0xfe
mov esi,0xc01
.create_kernel_pde:
mov [ebx+esi*4],eax
inc esi
add eax,0x1000
loop .create_kernel_pde
ret
home\robot\bochs\boot\boot.inc
;-------------loader----------------
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
PAGE_DIR_TABLE_POS equ 0x100000 ;页目录表的物理地址,1MB的第一个字节
;-------------------- gdt描述符属性 --------------------------------
DESC_G_4K equ 1_00000000000000000000000b ;第23位G 表示4K或者1MB位 段界限的单位值 此时为1则为4k
DESC_D_32 equ 1_0000000000000000000000b ;第22位D/B位 表示地址值用32位EIP寄存器 操作数与指令码32位
DESC_L equ 0_000000000000000000000b ;第21位 设置成0表示不设置成64位代码段 忽略
DESC_AVL equ 0_00000000000000000000b ;第20位 是软件可用的 操作系统额外提供的 可不设置
DESC_LIMIT_CODE2 equ 1111_0000000000000000b ;第16-19位 段界限的最后四位 全部初始化为1 因为最大段界限*粒度必须等于0xffffffff
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ;相同的值 数据段与代码段段界限相同
DESC_LIMIT_VIDEO2 equ 0000_0000000000000000b ;第16-19位 显存区描述符VIDEO2,这里的全是0为高位 低位即可表示段基址
DESC_P equ 1_000000000000000b ;第15位 P present判断段是否存在于内存
DESC_DPL_0 equ 00_0000000000000b ;第13-14位 这两位更是重量级 Privilege Level 0-3
DESC_DPL_1 equ 01_0000000000000b ;0为操作系统 权力最高 3为用户段 用于保护
DESC_DPL_2 equ 10_0000000000000b
DESC_DPL_3 equ 11_0000000000000b
DESC_S_sys equ 0_000000000000b ;第12位为0 则表示系统段 为1则表示数据段
DESC_S_CODE equ 1_000000000000b ;第12位与type字段结合 判断是否为系统段还是数据段
DESC_S_DATA equ DESC_S_CODE
DESC_TYPE_CODE equ 1000_00000000b ;第9-11位表示该段状态 1000 可执行 不允许可读 已访问位0
;x=1 c=0 r=0 a=0
DESC_TYPE_DATA equ 0010_00000000b ;第9-11位表示该段状态 0010 可写
;x=0 e=0 w=1 a=0
;代码段描述符高位4字节初始化 (0x00共8位 <<24 共32位初始化0)
;4KB为单位 Data段32位操作数 初始化的部分段界限 最高权限操作系统代码段 P存在表示 状态
DESC_CODE_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0X00
;数据段描述符高位4字节初始化
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
;显存段描述符高位4字节初始ua
DESC_VIDEO_HIGH4 equ (0x00<<24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0X0B
;-------------------- 选择子属性 --------------------------------
;第0-1位 RPL 特权级比较是否允许访问 第2位TI 0表示GDT 1表示LDT 第3-15位索引值
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b
;------------------- 页表相关属性 -------------------------------
;第1位,存在位
PG_P equ 1b
;第2位,读写位
PG_RW_R equ 00b
PG_RW_W equ 10b
;第3位,普通/超级用户位
PG_US_S equ 000b
PG_US_U equ 100b
评论