Prechádzať zdrojové kódy

:new: 完成了系统信息的获取、屏幕大小切换、cpu模式切换

fslongjin 3 rokov pred
rodič
commit
86aac18b25
2 zmenil súbory, kde vykonal 351 pridanie a 11 odobranie
  1. 0 2
      bootloader/boot.asm
  2. 351 9
      bootloader/loader.asm

+ 0 - 2
bootloader/boot.asm

@@ -297,5 +297,3 @@ LoaderFileName:		db	"LOADER  BIN",0 ;最后这个0是为了填满12字节的宽
     times 510 - ( $ - $$ ) db 0
     dw 0xaa55 ;===确保以0x55 0xaa为结尾
 
-
-

+ 351 - 9
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