2 Star 5 Fork 4

稀风 / KOS

Create your Gitee Account
Explore and code with more than 12 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Please pay attention to the specific project description and its upstream code dependency when using it.
Clone or Download
loader18.asm 16.07 KB
Copy Edit Raw Blame History
稀风 authored 2022-12-01 10:11 . 新增:获取物理内存容量逻辑
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
org 0x900
jmp CODE16_START
; 段属性的一些定义,暂时定义一些目前用得到的属性
DA_32 equ 0x4000 ; D/B = 1
DA_DR equ 0x90 ; 数据,只读
DA_DRW equ 0x92 ; 数据,可读/写
DA_DRWA equ 0x93 ; 数据,可读/写,已访问
DA_C equ 0x98 ; 代码,仅执行
DA_CR equ 0x9A ; 代码,可执行,可读
DA_CCO equ 0x9C ; 代码,一致性段,仅执行
DA_CCOR equ 0x9E ; 代码,一致性段,可执行,可读,已访问
DA_LIMIT_4K equ 0x8000 ; G = 1, 颗粒度:4K
; 段选择符属性定义
SA_RPL0 equ 0 ; RPL = 0
SA_RPL1 equ 1 ; RPL = 1
SA_RPL2 equ 2 ; RPL = 2
SA_RPL3 equ 3 ; RPL = 3
SA_TIG equ 0 ; TI = 0, GDT
SA_TIL equ 4 ; TI = 1, LDT
; 段描述符定义
%macro Descriptor 3 ; 有三个参数:段基址、段界限、段属性
dw %2 & 0xFFFF ; 段界限 1 (2 字节)
dw %1 & 0xFFFF ; 段基址 1 (2 字节)
db (%1 >> 16) & 0xFF ; 段基址 2 (1 字节)
dw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 段属性 1 + 段界限 2 + 段属性 2 (2 字节)
db (%1 >> 24) & 0xFF ; 段基址 3 (1 字节)
%endmacro ; 共 8 个字节
; 页目录表与页起始地址
PAGE_DIR_BASE0 equ 0x100000
PAGE_TAB_BASE0 equ 0x101000
PAGE_DIR_BASE1 equ 0x600000
PAGE_TAB_BASE1 equ 0x601000
; 页表相关属性
PG_P equ 1b ; 页存在属性位
PG_RW_R equ 00b ; R/W 属性位值, 读/执行
PG_RW_W equ 10b ; R/W 属性位值, 读/写/执行
PG_US_S equ 000b ; U/S 属性位值, 系统级
PG_US_U equ 100b ; U/S 属性位值, 用户级
; 全局描述符表定义
; 段基址 段界限 段属性
GDT_BASE : Descriptor 0, 0, 0
CODE32_DESC : Descriptor 0, CODE32_SEG_LEN - 1, DA_C + DA_32
VIDEO_DESC : Descriptor 0xB8000, 0xBFFFF - 0xB8000, DA_DRWA + DA_32
DATA_DESC : Descriptor 0, DATA_SEG_LEN - 1, DA_DR + DA_32
STACK32_DESC : Descriptor 0, TOP_OF_STACK32, DA_DRW + DA_32
PAGE_DIR_DESC0 : Descriptor PAGE_DIR_BASE0, 4095, DA_DRW + DA_32
PAGE_TAB_DESC0 : Descriptor PAGE_TAB_BASE0, 1023, DA_DRW + DA_32 + DA_LIMIT_4K
PAGE_DIR_DESC1 : Descriptor PAGE_DIR_BASE1, 4095, DA_DRW + DA_32
PAGE_TAB_DESC1 : Descriptor PAGE_TAB_BASE1, 1023, DA_DRW + DA_32 + DA_LIMIT_4K
FLAT_MODE_DESC : Descriptor 0, 0xFFFFF, DA_DRW + DA_32 + DA_LIMIT_4K
FLAT_MODE_C_DESC : Descriptor 0, 0xFFFFF, DA_C + DA_32 + DA_LIMIT_4K
; ...
GDT_LEN equ $ - GDT_BASE ; GDT 长度 = 当前地址 - GDT_BASE 地址
GDT_PTR :
dw GDT_LEN - 1
dd GDT_BASE
; 段选择符定义,RPL = 0; TI = 0
CODE32_SELECTOR equ (0x0001 << 3) + SA_RPL0 + SA_TIG
VIDEO_SELECTOR equ (0x0002 << 3) + SA_RPL0 + SA_TIG
DATA_SELECTOR equ (0x0003 << 3) + SA_RPL0 + SA_TIG
STACK32_SELECTOR equ (0x0004 << 3) + SA_RPL0 + SA_TIG
PAGE_DIR0_SELECTOR equ (0x0005 << 3) + SA_RPL0 + SA_TIG
PAGE_TAB0_SELECTOR equ (0x0006 << 3) + SA_RPL0 + SA_TIG
PAGE_DIR1_SELECTOR equ (0x0007 << 3) + SA_RPL0 + SA_TIG
PAGE_TAB1_SELECTOR equ (0x0008 << 3) + SA_RPL0 + SA_TIG
FLAT_MODE_SELECTOR equ (0x0009 << 3) + SA_RPL0 + SA_TIG
FLAT_MODE_C_SELECTOR equ (0x000A << 3) + SA_RPL0 + SA_TIG
msg db "Loader..." ; 使用 db 定义数据
msgLen equ $-msg ; 字符串 msg 长度
GET_MEM_ERR_DATA db "Get Memory Err" ; 使用 db 定义数据
GET_MEM_ERR_LEN equ $-GET_MEM_ERR_DATA ; 字符串 GET_MEM_ERR_DATA 长度
MEM_SIZE times 4 db 0
MEM_ARDS times 64 * 20 db 0 ; ARDS 缓冲区
[bits 16]
CODE16_START :
xor ax, ax ; xor 指令与 and or 指令类似,两个操作数的每一对对应位都应用如下操作原则:
; 如果两个位的值相同(同为 0 或同为 1),则结果位等于 0;否则结果位等于 1
; xor ax, ax 等于 ax = 0
mov ss, ax ; 栈段 ss = 0
mov ds, ax ; 数据段 ds = 0
mov es, ax ; 附加数据段 es = 0
mov sp, 0x900 ; 设置栈,跟 0x7c00 一样,0x900 地址以下也有一段安全可用内存
; 设置光标位置 (dl, dh)
mov ah, 0x02 ; AH 功能号 = 0x02,设置光标
mov dh, 0x01 ; 设置光标行号
mov dl, 0x00 ; 设置光标列号
mov bh, 0x00 ; 页码
int 0x10
; 打印 msg
mov ax, msg
mov cx, msgLen
call print
; 设置光标位置 (dl, dh)
mov ah, 0x02 ; AH 功能号 = 0x02,设置光标
mov dh, 0x02 ; 设置光标行号
mov dl, 0x00 ; 设置光标列号
mov bh, 0x00 ; 页码
int 0x10
; call detect_memory_0xe801
; call detect_memory_0xe820
call detect_memory
; 初始化段描述符中的段基址
mov esi, CODE32_START
mov edi, CODE32_DESC
call InitDescItem
mov esi, DATA_SEGMENT
mov edi, DATA_DESC
call InitDescItem
mov esi, STACK32_SEGMENT
mov edi, STACK32_DESC
call InitDescItem
; 打开 A20 地址线
in al, 0x92
or al, 0000_0010B
out 0x92, al
; 加载描述符表
;mov eax, 0
;mov ax, ds
;shl eax, 4
;add eax, GDT_BASE
;mov dword [GDT_PTR + 2], eax
lgdt [GDT_PTR]
; 通知 CPU 进入保护模式,即将 CR0 寄存器的 PE(bit0) 位置 1
mov eax, cr0
or eax, 0x01
mov cr0, eax
; 刷新流水线
; 跳转到 32 位代码段继续执行
jmp dword CODE32_SELECTOR:0
; 参数: esi --> 代码段标签
; 参数: edi --> 段描述符标签
InitDescItem:
push eax
mov eax, 0
mov ax, cs
shl eax, 4
add eax, esi
mov word [edi + 2], ax
shr eax, 16
mov byte [edi + 4], al
mov byte [edi + 7], ah
pop eax
ret
; 打印字符串
; ax : 输入参数,字符串地址
; cx : 输入参数,字符串长度
print:
; 入栈
push ax
push bp
push bx
mov bp, ax ; 保存字符串地址
mov ax, 0x1301 ; 子功能号 0x13 是写字符串
mov bx, 000fh ; 页号位 0,黑底白字
int 0x10
; 出栈
pop bx
pop bp
pop ax
ret
detect_memory_0xe801:
push eax
push ebx
push ecx
push edx
mov dword [MEM_SIZE], 0 ; 将 MEM_SIZE 清零
xor eax, eax ; 用于清 CF 标志位 CF = 0
mov eax, 0xE801
int 0x15
jc .err ; 如果 CF = 1,表示读取内存容量失败,跳转到 .err 处执行
shl eax, 10 ; eax = eax * 1024
shl ebx, 16 ; ebx = ebx * 64 * 1024
add dword [MEM_SIZE], eax
add dword [MEM_SIZE], ebx
mov ecx, 1
shl ecx, 20 ; ecx = 1MB
add dword [MEM_SIZE], ecx ; 人为增加 1M
jmp .ok
.err:
mov dword [MEM_SIZE], 0 ; 将 MEM_SIZE 清零
.ok:
pop edx
pop ecx
pop ebx
pop eax
ret
detect_memory_0xe820:
push edi
push ebx
push ecx
push edx
mov di, MEM_ARDS
mov ebx, 0
.loop:
; 固定参数
mov eax, 0xE820
mov edx, 0x534D4150
mov ecx, 20
int 0x15
jc .err
cmp dword [edi + 16], 1 ; 比较 Type 是否为 1
jne .next ; 如果不相等,跳转到 .next
mov eax, [edi] ; BaseAddrLow
add eax, [edi + 8] ; BaseAddrLow + LengthLow
cmp dword [MEM_SIZE], eax; 比较 [MEM_SIZE] 和 BaseAddrLow + LengthLow 大小
jnb .next ; 如果 [MEM_SIZE] < BaseAddrLow + LengthLow,跳转到 .next
mov dword [MEM_SIZE], eax; 此时 BaseAddrLow + LengthLow 较大,将较大的值写入 [MEM_SIZE] 中
.next:
add edi, 20
cmp ebx, 0
jne .loop ; 结束循环条件:ebx = 0
; 程序运行到这里说明 MEM_SIZE 已得到,跳转到 .ok 处执行
; 实测发现得到的 MEM_SIZE 始终比实际少 0x10000,也不知道为啥,干脆就像 e801 一样人为补一下吧
mov ecx, 1
shl ecx, 16 ; ecx = 0x10000
add dword [MEM_SIZE], ecx ; 人为增加 0x10000
jmp .ok
.err:
mov dword [MEM_SIZE], 0 ; 将 MEM_SIZE 清零
.ok:
pop edx
pop ecx
pop ebx
pop edi
ret
detect_memory:
push ax
push cx
call detect_memory_0xe820
cmp dword [MEM_SIZE], 0
jne .ok ; 如果 [MEM_SIZE] 不为 0,表明获取物理内存成功
call detect_memory_0xe801
cmp dword [MEM_SIZE], 0
jne .ok ; 如果 [MEM_SIZE] 不为 0,表明获取物理内存成功
.err:
; 打印 "Get Memory Err"
mov ax, GET_MEM_ERR_DATA
mov cx, GET_MEM_ERR_LEN
call print
jmp $ ; 死机
.ok:
pop cx
pop ax
ret
[bits 32]
CODE32_START :
; 设置栈寄存器
mov ax, STACK32_SELECTOR
mov ss, ax
mov eax, TOP_OF_STACK32
mov esp, eax
; 设置显存段
mov ax, VIDEO_SELECTOR
mov gs, ax
; 设置数据段
mov ax, DATA_SELECTOR
mov ds, ax
; mov eax, 0
; mov es, eax
mov eax, FLAT_MODE_SELECTOR
mov es, eax
mov ebp, msg2Offset
mov bl, 0x0F ; 打印属性,黑底白字
; 坐标 (0, 2)
mov dl, 0x00
mov dh, 0x02
call print_str_32
mov eax, PAGE_DIR0_SELECTOR
mov ebx, PAGE_TAB0_SELECTOR
mov ecx, PAGE_TAB_BASE0
call Init_Page_Table
mov eax, PAGE_DIR1_SELECTOR
mov ebx, PAGE_TAB1_SELECTOR
mov ecx, PAGE_TAB_BASE1
call Init_Page_Table
; 临时把 ds 设置为平坦模式(可读写)段
mov ax, FLAT_MODE_SELECTOR
mov ds, ax
mov esi, test_func1 ; 源
mov edi, 0xD01000 ; 目标
mov ecx, test_func1_len ; 循环次数
call MemCpy32
mov esi, test_func2 ; 源
mov edi, 0xE01000 ; 目标
mov ecx, test_func2_len ; 循环次数
call MemCpy32
mov ax, DATA_SELECTOR
mov ds, ax
; mov eax, 0xD01007
; mov [es:0x102004], eax ; 将数值 0xD01007 写入 0x102004 地址处
mov eax, 0x401000
mov ebx, 0xD01000
mov ecx, PAGE_DIR_BASE0
call MapAddress
; mov eax, 0xE01007
; mov [es:0x602004], eax ; 将数值 0xE01007 写入 0x106004 地址处
mov eax, 0x401000
mov ebx, 0xE01000
mov ecx, PAGE_DIR_BASE1
call MapAddress
mov eax, PAGE_DIR_BASE0
call Switch_Page_Table
; call FLAT_MODE_SELECTOR : 0x401000
call FLAT_MODE_C_SELECTOR : 0x401000
mov eax, PAGE_DIR_BASE1
call Switch_Page_Table
; call FLAT_MODE_SELECTOR : 0x401000
call FLAT_MODE_C_SELECTOR : 0x401000
jmp $
; 使用显存方式打印字符串
; ds:ebp --> 打印的数据起始地址(相对于段基址的偏移地址)
; bl --> 打印属性
; dx --> 打印起始坐标 (dl, dh)
print_str_32:
push ebp
push eax
push edi
push cx
push dx
; 循环
s:
mov cl, [ds:ebp] ; 取地址 ds:ebp 中的数据存到 cl 寄存器
cmp cl, 0 ; 比较 cl 是否为 0
je end ; 若为 0 ,就结束打印
; 根据坐标 (dl, dh) 计算出偏移量,存入 eax 中
; (80 * dh + dl)*2 ; 每行最多显示 80 个字符
mov eax, 80
mul dh
add al, dl
shl eax, 1 ; eax = eax*2
mov edi, eax ; edi :显存中的偏移量
mov ah, bl ; 显示属性
mov al, cl ; 要打印的字符
mov [gs:edi], ax ; 显示数据放入显存
inc ebp ; 自增
inc dl ; 自增
jmp s ; 循环
; 打印结束
end:
pop dx
pop cx
pop edi
pop eax
pop ebp
ret
; 功能:创建页目录表及页表
; 传入参数:
; eax --> 页目录选择子
; ebx --> 页表选择子
; ecx --> 页表基地址
Init_Page_Table:
push es
push eax ; [esp + 12]
push ebx ; [esp + 8]
push ecx ; [esp + 4]
push edi ; [esp], esp 栈顶指针,始终指向最后一次入栈处,此处 [esp] = edi
; 创建页目录
mov es, eax ; eax ==> 页目录选择子
mov edi, 0 ; es:edi ==> 页目录选择子:0
mov ecx, 1024 ; 循环 1024 次,页目录共 1024 项
mov eax, [esp + 4] ;
or eax, PG_P | PG_US_U | PG_RW_W ; eax = 第一个子页表的地址和属性
cld ; edi 自增
.creat_pde: ; 创建页目录项
stosd ; 把 eax 中的值写入 [es:edi] 指向的内存中
add eax, 4096 ; eax = eax + 4K
loop .creat_pde
; 程序执行到这里说明页目录表已经创建完成
; 创建页表
mov ax, [esp + 8] ; 页表选择子
mov es, eax
mov edi, 0 ; es:edi ==> 页表选择子:0
mov ecx, 1024*1024 ; 循环 1024*1024 次,共 1024 个页表,每个页表有 1024 个页表项
mov eax, PG_P | PG_US_U | PG_RW_W ; 只有属性,没有基地址,因为第一个页表项指向的就是物理地址 0x0 处
cld
.creat_pte: ; 创建页表项
stosd ; 把 eax 中的值写入 [es:edi] 指向的内存中
add eax, 4096 ; eax = eax + 4K
loop .creat_pte
pop edi
pop ecx
pop ebx
pop eax
pop es
ret
; 功能:切换页表
; 传入参数:
; eax --> 页目录基地址
Switch_Page_Table:
push eax
; 寄存器 cr0 的 PG 位清 0,暂时先关闭内存分页功能
mov eax, cr0
and eax, 0x7FFFFFFF ; and 按位与
mov cr0, eax
; 将页目录表首地址写入控制寄存器 cr3
mov eax, [esp] ; [esp] = Switch_Page_Table 运行前 eax
mov cr3, eax
; 寄存器 cr0 的 PG 位置 1,开启内存分页
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
pop eax
ret
; ds:esi --> 源
; es:edi --> 目标
; ecx --> 循环次数,即要拷贝的字节数
MemCpy32:
push eax
push edi
push esi
push ecx
.s: mov al, [ds:esi] ; [ds:esi] : 源地址
mov [es:edi], al ; [es:edi] : 目标地址
inc edi ; edi = edi + 1
inc esi ; esi = esi +1
loop .s ; 循环次数由 ecx 决定
pop ecx
pop esi
pop edi
pop esi
ret
; es --> 平坦模式下使用,es 为平坦模式段选择子
; eax --> 虚拟地址
; ebx --> 目标物理地址
; ecx --> 页目录表基地址
MapAddress:
push edi
push esi
push eax ; [esp + 8]
push ebx ; [esp + 4]
push ecx ; [esp]
; 1. 取虚地址高 10 位, 计算子页表在页目录中的位置
mov eax, [esp + 8]
shr eax, 22
and eax, 1111111111b
shl eax, 2
; 2. 取虚地址中间 10 位, 计算物理地址在子页表中的位置
mov ebx, [esp + 8]
shr ebx, 12
and ebx, 1111111111b
shl ebx, 2
; 3. 取子页表起始地址
mov esi, [esp]
add esi, eax
mov edi, [es:esi]
and edi, 0xFFFFF000
; 4. 将目标地址写入子页表的对应位置
add edi, ebx
mov ecx, [esp + 4]
and ecx, 0xFFFFF000
or ecx, PG_P | PG_US_U | PG_RW_W
mov [es:edi], ecx
pop ecx
pop ebx
pop eax
pop esi
pop edi
ret
test_func1:
mov eax, 8
retf
test_func1_len equ $ - test_func1
test_func2:
mov eax, 9
retf
test_func2_len equ $ - test_func2
CODE32_SEG_LEN equ $-CODE32_START
DATA_SEGMENT:
msg2 db "Enter protection", 0 ; 以 0 为字符串结束标志
msg2Offset equ msg2 - DATA_SEGMENT ; msg2 在数据段 DATA_SEGMENT 中的偏移量
TEST_DATA_ABCD db "ABCD" ; 数据 "ABCD"
TEST_ABCD_OFFSET equ TEST_DATA_ABCD - DATA_SEGMENT ; "ABCD" 在数据段 DATA_SEGMENT 中的偏移量
TEST_DATA_abcd db "abcd" ; 数据 "abcd"
TEST_abcd_OFFSET equ TEST_DATA_abcd - DATA_SEGMENT ; "abcd" 在数据段 DATA_SEGMENT 中的偏移量
DATA_SEG_LEN equ $ - DATA_SEGMENT
STACK32_SEGMENT:
times 1024 * 4 db 0 ; 开辟 4K 的内存空间当做栈
STACK32_SEG_LEN equ $ - STACK32_SEGMENT
TOP_OF_STACK32 equ STACK32_SEG_LEN - 1 ; 栈顶
1
https://gitee.com/thin-wind/KOS.git
git@gitee.com:thin-wind/KOS.git
thin-wind
KOS
KOS
main

Search