From 86aac18b25b6105425c03f0b8048f351832d6449 Mon Sep 17 00:00:00 2001 From: fslongjin Date: Wed, 19 Jan 2022 22:32:54 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20=E5=AE=8C=E6=88=90=E4=BA=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E4=BF=A1=E6=81=AF=E7=9A=84=E8=8E=B7=E5=8F=96=E3=80=81?= =?UTF-8?q?=E5=B1=8F=E5=B9=95=E5=A4=A7=E5=B0=8F=E5=88=87=E6=8D=A2=E3=80=81?= =?UTF-8?q?cpu=E6=A8=A1=E5=BC=8F=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bootloader/boot.asm | 2 - bootloader/loader.asm | 360 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 351 insertions(+), 11 deletions(-) diff --git a/bootloader/boot.asm b/bootloader/boot.asm index 375c3ee0..f0f9722e 100644 --- a/bootloader/boot.asm +++ b/bootloader/boot.asm @@ -297,5 +297,3 @@ LoaderFileName: db "LOADER BIN",0 ;最后这个0是为了填满12字节的宽 times 510 - ( $ - $$ ) db 0 dw 0xaa55 ;===确保以0x55 0xaa为结尾 - - diff --git a/bootloader/loader.asm b/bootloader/loader.asm index 42b3519a..43d4e9d6 100644 --- a/bootloader/loader.asm +++ b/bootloader/loader.asm @@ -17,20 +17,37 @@ Offset_Tmp_Of_Kernel_File equ 0x7e00 ; 内核程序的临时转存空间 Memory_Struct_Buffer_Addr equ 0x7e00 ; 内核被转移到最终的内存空间后,原来的临时空间就作为内存结构数据的存储空间 - +; ==== 临时的全局描述符表 ===== [SECTION gdt] LABEL_GDT: dd 0,0 -LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00 +LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00 ; 代码段和数据段的段基地址都设置在0x00000000处, 把段限长设置为0xffffffff,可以索引32位地址空间 LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200 GdtLen equ $ - LABEL_GDT +; GDTR寄存器是一个6B的结构,低2B保存GDT的长度, 高4B保存GDT的基地址 GdtPtr dw GdtLen - 1 dd LABEL_GDT +; 这是两个段选择子,是段描述符在GDT表中的索引号 SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorData32 equ LABEL_DESC_DATA32 - LABEL_GDT +; === IA-32e模式的临时gdt表 +[SECTION gdt64] +LABEL_GDT64: dq 0x0000000000000000 +LABEL_DESC_CODE64: dq 0x0020980000000000 +LABEL_DESC_DATA64: dq 0x0000920000000000 + +GdtLen64 equ $ - LABEL_GDT64 +GdtPtr64 dw GdtLen64-1, + dd LABEL_GDT64 + +SelectorCode64 equ LABEL_DESC_CODE64 - LABEL_GDT64 +SelectorData64 equ LABEL_DESC_DATA64 - LABEL_GDT64 + + + [SECTION .s16] ;定义一个名为.s16的段 [BITS 16] ; 通知nasm,将要运行在16位宽的处理器上 @@ -349,7 +366,7 @@ Label_Get_Mem_OK: mov ax, 0x1301 mov bx, 0x000f mov dx, 0x0400 ; 在第5行显示 - mov cx, 39 + mov cx, 38 push ax mov ax, ds mov es, ax @@ -364,16 +381,290 @@ Label_Get_SVGA_Info: mov ax, 0x1301 mov bx, 0x000f mov dx, 0x0500 ; 在第6行显示 - mov cx, 30 + mov cx, 34 push ax mov ax, ds mov es, ax pop ax - mov bp, Message_Start_Get_SVGA_Info + mov bp, Message_Start_Get_SVGA_VBE_Info + int 0x10 + + ; 使用INT0x10的主功能号0x4F00获取SVGA VBE信息 + ; For more information, please visit: https://longjin666.top/?p=1321 + mov ax, 0x00 + mov es, ax + mov di, 0x8000 + mov ax, 0x4F00 + int 0x10 + + cmp ax, 0x004F ; 获取成功 + jz Label_Get_SVGA_VBE_Success + +Label_Get_SVGA_VBE_Failed: + ; 获取SVGA VBE信息失败 + mov ax, 0x1301 + mov bx, 0x008c + mov dx, 0x0600 ; 在第7行显示 + mov cx, 33 + push ax + mov ax, ds + mov es, ax + pop ax + mov bp, Message_Get_SVGA_VBE_Failed + int 0x10 + jmp $ + +Label_Get_SVGA_VBE_Success: + mov ax, 0x1301 + mov bx, 0x000f + mov dx, 0x0600 ; 在第7行显示 + mov cx, 38 + push ax + mov ax, ds + mov es, ax + pop ax + mov bp, Message_Get_SVGA_VBE_Success + int 0x10 + +Label_Get_SVGA_Mode_Info: + ; ====== 获取SVGA mode信息 ====== + mov ax, 0x1301 + mov bx, 0x000f + mov dx, 0x0700 ; 在第8行显示 + mov cx, 35 + push ax + mov ax, ds + mov es, ax + pop ax + mov bp, Message_Start_Get_SVGA_Mode_Info + int 0x10 + + mov ax, 0x00 + mov es, ax + mov si, 0x800e ; 根据文档可知,偏移量0Eh处, DWORD pointer to list of supported VESA and OEM video modes + ;(list of words terminated with FFFFh) + mov esi, dword [es:si] + mov edi, 0x8200 + +Label_SVGA_Mode_Info_Get: + mov cx, word [es:esi] + + +; ===========显示SVGA mode的信息 + ;push ax + + ;mov ax, 0x00 + ;mov al, ch + ;call Label_DispAL + + ;mov ax, 0x00 + ;mov al, cl + ;call Label_DispAL + + ;pop ax +;============ + + ; 判断是否获取完毕 + cmp cx, 0xFFFF + jz Label_SVGA_Mode_Info_Finish + + mov ax, 0x4f01 ; 使用4f01功能,获取SVGA的模式 + int 0x10 + + cmp ax, 0x004f ; 判断是否获取成功 + jnz Label_SVGA_Mode_Info_Fail + + add esi, 2 + add edi, 0x100 ; 开辟一个 256-byte 的 buffer + + jmp Label_SVGA_Mode_Info_Get + +Label_SVGA_Mode_Info_Fail: + ; === 获取信息失败 === + mov ax, 0x1301 + mov bx, 0x008c + mov dx, 0x0800 ; 在第9行显示 + mov cx, 34 + push ax + mov ax, ds + mov es, ax + pop ax + mov bp, Message_Get_SVGA_Mode_Failed int 0x10 jmp $ +Label_SVGA_Mode_Info_Finish: + ; === 成功获取SVGA mode信息 === + mov ax, 0x1301 + mov bx, 0x000f + mov dx, 0x0800 ; 在第9行显示 + mov cx, 39 + push ax + mov ax, ds + mov es, ax + pop ax + mov bp, Message_Get_SVGA_Mode_Success + int 0x10 + jmp Label_Set_SVGA_Mode + +Label_SET_SVGA_Mode_VESA_VBE_FAIL: + ; 设置SVGA显示模式失败 + mov ax, 0x1301 + mov bx, 0x008c + mov dx, 0x0800 ; 在第10行显示 + mov cx, 29 + push ax + mov ax, ds + mov es, ax + pop ax + mov bp, Message_Set_SVGA_Mode_Failed + int 0x10 + + jmp $ + + +Label_Set_SVGA_Mode: + +; ===== 设置SVGA芯片的显示模式(VESA VBE) === + mov ax, 0x4f02 ; 使用int0x10 功能号AX=4f02设置SVGA芯片的显示模式 + mov bx, 0x4180 ; 显示模式可以选择0x180(1440*900 32bit)或者0x143(800*600 32bit) + int 0x10 + + cmp ax, 0x004F + jnz Label_SET_SVGA_Mode_VESA_VBE_FAIL + +; ===== 初始化GDT表,切换到保护模式 ===== + + cli ; 关闭外部中断 + db 0x66 + lgdt [GdtPtr] + + db 0x66 + lidt [IDT_POINTER] + + mov eax, cr0 + or eax, 1 ; 启用保护模式 + mov cr0, eax + + ; 跳转到保护模式下的第一个程序 + jmp dword SelectorCode32:GO_TO_TMP_Protect + + +[SECTION .s32] +[BITS 32] +GO_TO_TMP_Protect: + ; ==== 切换到长模式 ===== + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov ss, ax + mov esp, 0x7e00 ; 将栈指针设置在实模式获取到的数据的基地址上 + + call support_long_mode ; 检测是否支持长模式 + + test eax, eax ; 将eax自身相与,检测是否为0(test指令不会把结果赋值回去eax) + jz no_support ; 不支持长模式 + + ; 初始化临时页表, 基地址设置为0x90000 + ; 设置各级页表项的值(页表起始地址与页属性组成) + mov dword [0x90000], 0x91007 + mov dword [0x90004], 0x00000 + mov dword [0x90800], 0x91007 + mov dword [0x90804], 0x00000 + + mov dword [0x91000], 0x92007 + mov dword [0x91004], 0x00000 + + mov dword [0x92000], 0x000083 + mov dword [0x92004], 0x000000 + + mov dword [0x92008], 0x200083 + mov dword [0x9200c], 0x000000 + + mov dword [0x92010], 0x400083 + mov dword [0x92014], 0x000000 + + mov dword [0x92018], 0x600083 + mov dword [0x9201c], 0x000000 + + mov dword [0x92020], 0x800083 + mov dword [0x92024], 0x000000 + + mov dword [0x92028], 0xa00083 + mov dword [0x9202c], 0x000000 + + ; === 加载GDT === + db 0x66 + lgdt [GdtPtr64] ; 加载GDT + ; 把临时gdt的数据段加载到寄存器中(cs除外) + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + mov esp, 0x7e00 + + ; ====== 开启物理地址扩展 ===== + ; 通过bts指令,将cr4第5位置位,开启PAE + mov eax, cr4 + bts eax, 5 + mov cr4, eax + + ; 将临时页目录的地址设置到CR3控制寄存器中 + mov eax, 0x90000 + mov cr3, eax + + ; ==== 启用长模式 === + ; 参见英特尔开发手册合集p4360 volume4, chapter2 2-60 Vol. 4 + ; IA32_EFER寄存器的第8位是LME标志位,能启用IA-32e模式 + mov ecx, 0xC0000080 + rdmsr + bts eax, 8 + wrmsr + + ; === 开启分页机制 === + mov eax, cr0 + bts eax, 0 ; 再次开启保护模式 + bts eax, 31 ; 开启分页管理机制 + mov cr0, eax + + + ; === 通过此条远跳转指令,处理器跳转到内核文件进行执行,正式进入IA-32e模式 + + jmp SelectorCode64:Offset_Of_Kernel_File + + +support_long_mode: + ; ===== 检测是否支持长模式 ==== + mov eax, 0x80000000 + cpuid ; cpuid指令返回的信息取决于eax的值。当前返回到eax中的是最大的输入参数值。 详见:英特尔开发人员手册卷2A Chapter3 (Page 304) + cmp eax, 0x80000001 + setnb al ; 当cmp结果为不低于时,置位al + jb support_long_mode_done ; 当eax小于0x80000001时,跳转 + + mov eax, 0x80000001 + cpuid ; 获取特定信息,参照开发人员手册卷2A p304 + + bt edx, 29 ; 将edx第29位的值移到CF上。该位指示了CPU是否支持IA-32e模式 + ; Bit 29: Intel® 64 Architecture available if 1. + setc al ; 若支持则al置位 + +support_long_mode_done: + movzx eax, al ; 将al,零扩展为32位赋值给eax + ret + +no_support: + ; 不支持长模式 + jmp $ + + + +[SECTION .s16lib] +[BITS 16] ; 从软盘读取一个扇区 ; AX=待读取的磁盘起始扇区号 ; CL=读入的扇区数量 @@ -471,24 +762,75 @@ Label_Even_2: pop es ret +; ==== 显示AL中的信息 === +Label_DispAL: + push ecx + push edx + push edi + mov edi, [DisplayPosition] + mov ah, 0x0F + mov dl, al ; 为了先显示al的高4位,因此先将al暂存在dl中,然后把al往右移动4位 + shr al, 4 + mov ecx, 2 ; 计数为2 +.begin: + and al, 0x0F + cmp al, 9 + ja .1 ; 大于9,跳转到.1 + add al, '0' + jmp .2 +.1: + sub al, 0x0a + add al, 'A' +.2: + ; 移动到显示内存中 + mov [gs:edi], ax + add edi, 2 + mov al, dl + loop .begin -;====临时变量===== + mov [DisplayPosition], edi + + pop edi + pop edx + pop ecx + + ret + +; === 临时的中断描述符表 === +; 为临时的IDT开辟空间。 +; 由于模式切换过程中已经关闭了外部中断,只要确保模式切换过程中不产生异常,就不用完整的初始化IDT。甚至乎,只要没有异常产生,没有IDT也可以。 +IDT: + times 0x50 dq 0 +IDT_END: + +IDT_POINTER: + dw IDT_END - IDT - 1 + dd IDT + +;==== 临时变量 ===== RootDirSizeForLoop dw RootDirSectors SectorNo dw 0 Odd db 0 OffsetOfKernelFileCount dd Offset_Of_Kernel_File +DisplayPosition dd 0 + ; 要显示的消息文本 Message_Start_Loader: db "[DragonOS] Start Loader" Message_No_Loader: db "[ERROR] No Kernel Found." Message_Kernel_Loaded: db "[INFO] Kernel loaded" Message_Start_Get_Mem_Struct: db "[INFO] Try to get memory struct..." Message_Get_Mem_Failed: db "[ERROR] Get memory struct failed." -Message_Get_Mem_Success: db "[INFO] Successful to get memory struct." -Message_Start_Get_SVGA_Info: db "[INFO] Try to get SVGA info..." - +Message_Get_Mem_Success: db "[INFO] Successfully got memory struct." +Message_Start_Get_SVGA_VBE_Info: db "[INFO] Try to get SVGA VBE info..." +Message_Get_SVGA_VBE_Failed: db "[ERROR] Get SVGA VBE info failed." +Message_Get_SVGA_VBE_Success: db "[INFO] Successfully got SVGA VBE info." +Message_Start_Get_SVGA_Mode_Info: db "[INFO] Try to get SVGA mode info..." +Message_Get_SVGA_Mode_Failed: db "[ERROR] Get SVGA Mode info failed." +Message_Get_SVGA_Mode_Success: db "[INFO] Successfully got SVGA Mode info." +Message_Set_SVGA_Mode_Failed: db "[ERROR] Set SVGA Mode failed." Kernel_FileName: db "KERNEL BIN", 0 \ No newline at end of file