Browse Source

:new: 切换为grub2引导,能进入Start_Kernel函数(未能完成初始化)

fslongjin 3 years ago
parent
commit
0b0cce9326

+ 1 - 0
.gitignore

@@ -1,2 +1,3 @@
 /cmake-build-debug/
 /bin/
+./DragonOS.iso

+ 2 - 1
Makefile

@@ -1,7 +1,8 @@
-SUBDIRS = bootloader kernel
+SUBDIRS = kernel
 
 .PHONY: all
 all:
+	mkdir -p bin/kernel/
 	@list='$(SUBDIRS)'; for subdir in $$list; do \
     		echo "make all in $$subdir";\
     		cd $$subdir;\

+ 7 - 6
bochsrc

@@ -5,16 +5,17 @@ config_interface: textconfig
 #memory: host=2048, guest=2048
 romimage: file="/usr/local/share/bochs/BIOS-bochs-latest"
 vgaromimage: file="/usr/local/share/bochs/VGABIOS-lgpl-latest"
-boot: floppy
-floppy_bootsig_check: disabled=0
-floppya: type=1_44, 1_44="bin/boot.img", status=inserted, write_protected=0
+# ata0-master: type=disk, path="/data/home/longjin/code/hd.img", mode=flat
+
+boot: cdrom
+
 # no floppyb
 ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
 ata0-master: type=none
 ata0-slave: type=none
-ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
-ata1-master: type=none
-ata1-slave: type=none
+
+ata1-master: type=cdrom, path="DragonOS.iso", status=inserted
+
 ata2: enabled=0
 ata3: enabled=0
 pci: enabled=1, chipset=i440fx

BIN
boot(empty).img


+ 0 - 11
bootloader/Makefile

@@ -1,11 +0,0 @@
-all: boot.bin loader.bin
-
-boot.bin: boot.asm
-	nasm boot.asm -o ../bin/bootloader/boot.bin
-
-loader.bin: loader.asm
-	nasm loader.asm -o ../bin/bootloader/loader.bin
-
-
-clean:
-	rm -rf *.asm~ Makefile~

+ 0 - 299
bootloader/boot.asm

@@ -1,299 +0,0 @@
-;将程序开始位置设置为0x7c00处,并给BaseOfStack赋值为0x7c00
-    org 0x7c00
-
-BaseOfStack	equ	0x7c00
-BaseOfLoader equ 0x1000
-OffsetOfLoader equ 0x00
-
-RootDirSectors equ 14   ;根目录占用的扇区数
-SectorNumOfRootDirStart equ 19  ; 根目录的起始扇区号
-SectorNumOfFAT1Start equ 1  ; FAT1表的起始扇区号 (因为前面有一个保留扇区(引导扇区))
-SectorBalance equ 17    ;平衡文件/目录的起始簇号与数据区域的起始簇号的差值。
-
-
-    jmp short Label_Start
-    nop
-    BS_OEMName  db  'DragonOS'
-    BPB_BytesPerSec dw 512
-    BPB_SecPerClus db 1
-    BPB_RsvdSecCnt  dw  1
-    BPB_NumFATs db 2
-    BPB_RootEntCnt dw 224
-    BPB_TotSec16 dw 2880
-    BPB_Media db 0xf0
-    BPB_FATSz16 dw 9
-    BPB_SecPerTrk dw 18
-    BPB_NumHeads dw 2
-    BPB_HiddSec dd 0
-    BPB_TotSec32 dd 0
-    BS_DrvNum db 0
-    BS_Reserved1 db 0
-    BS_BootSig db 0x29
-    BS_VolID dd 0
-    BS_VolLab db 'boot loader'
-    BS_FileSysType db 'FAT12   '
-
-
-
-Label_Start:
-    ;初始化寄存器
-    mov ax, cs
-    mov ds, ax
-    mov es, ax
-    mov ss, ax
-    mov sp, BaseOfStack
-
-    ;清屏
-    mov ax, 0x0600  ;AL=0时,清屏,BX、CX、DX不起作用
-    mov bx, 0x0700  ;设置白色字体,不闪烁,字体正常亮度,黑色背景
-    mov cx, 0
-    mov dx, 0184fh
-    int 0x10
-
-    ;设置屏幕光标位置为左上角(0,0)的位置
-    mov ax, 0x0200
-    mov bx, 0x0000
-    mov dx, 0x0000
-    int 10h
-
-    ;在屏幕上显示Start Booting
-    mov ax, 0x1301 ;设置显示字符串,显示后,光标移到字符串末端
-    mov bx, 0x000a ;设置黑色背景,白色字体,高亮度,不闪烁
-    mov dx, 0x0000 ;设置游标行列号均为0
-    mov cx, 24 ;设置字符串长度为24
-
-    push ax
-    mov ax, ds
-    mov es, ax
-    pop ax
-    mov bp, StartBootMessage
-    int 0x10
-
-    ;软盘驱动器复位
-    xor ah, ah
-    xor dl, dl
-    int 0x13
-
-; 在文件系统中搜索 loader.bin
-    mov word [SectorNo], SectorNumOfRootDirStart    ;保存根目录起始扇区号
-
-Label_Search_In_Root_Dir_Begin:
-    cmp word [RootDirSizeForLoop],  0 ; 比较根目录扇区数量和0的关系。 cmp实际上是进行了一个减法运算
-    jz Label_No_LoaderBin ; 等于0,不存在Loader.bin
-    dec word [RootDirSizeForLoop]
-
-    mov ax, 0x00
-    mov es, ax
-    mov bx, 0x8000
-    mov ax, [SectorNo]  ;向函数传入扇区号
-    mov cl, 1
-    call Func_ReadOneSector
-    mov si, LoaderFileName ;向源变址寄存器传入Loader文件的名字
-    mov di, 0x8000
-    cld ;由于LODSB的加载方向与DF标志位有关,因此需要用CLD清零DF标志位
-
-    mov dx, 0x10 ; 每个扇区的目录项的最大条数是(512/32=16,也就是0x10)
-
-Label_Search_For_LoaderBin:
-    cmp dx, 0
-    jz Label_Goto_Next_Sector_In_Root_Dir
-    dec dx
-    mov cx, 11 ; cx寄存器存储目录项的文件名长度, 11B,包括了文件名和扩展名,但是不包括 分隔符'.'
-
-Label_Cmp_FileName:
-    cmp cx, 0
-    jz Label_FileName_Found
-    dec cx
-    lodsb ; 把si对应的字节载入al寄存器中,然后,由于DF为0,si寄存器自增
-    cmp al, byte [es:di]    ; 间接取址[es+di]。   也就是进行比较当前文件的名字对应字节和loader文件名对应字节
-    jz  Label_Go_On     ; 对应字节相同
-    jmp Label_Different     ; 字节不同,不是同一个文件
-
-Label_Go_On:
-    inc di
-    jmp Label_Cmp_FileName
-
-Label_Different:
-    and di, 0xffe0 ;将di恢复到当前目录项的第0字节
-    add di, 0x20     ;将di跳转到下一目录项的第0字节
-    mov si, LoaderFileName
-    jmp Label_Search_For_LoaderBin  ;继续搜索下一目录项
-
-Label_Goto_Next_Sector_In_Root_Dir:
-    add word [SectorNo], 1
-    jmp Label_Search_In_Root_Dir_Begin
-
-Label_No_LoaderBin:
-    ; 在屏幕上显示 [ERROR] No Loader Found.
-    mov ax, 0x1301
-    mov bx, 0x000c  ; 红色闪烁高亮黑底
-    mov dx, 0x0100  ; 显示在第二行(前面已经显示过一行了)
-    mov cx, 24  ; 字符串长度
-    push ax
-    mov ax, ds
-    mov es, ax
-    pop ax
-    mov bp, NoLoaderMessage
-    int 0x10
-    jmp $
-
-
-;========== 找到了Loader.Bin
-
-Label_FileName_Found:
-    mov ax, RootDirSectors
-
-    ; 先取得目录项DIR_FstClus字段的值(起始簇号)
-    and di, 0xffe0
-    add di, 0x1a
-    mov cx, word    [es:di]
-    push cx
-
-
-    add cx, ax
-    add cx, SectorBalance
-    mov ax, BaseOfLoader
-    mov es, ax ;配置es和bx,指定loader.bin在内存中的起始地址
-    mov bx, OffsetOfLoader
-    mov ax, cx
-
-Label_Go_On_Loading_File:
-    push ax
-    push bx
-
-    ; 显示字符.
-    mov ah, 0x0e
-    mov al, "."
-    mov bl, 0x0f
-    int 0x10
-
-    pop bx
-    pop ax
-
-
-    ; 每读取一个扇区,就获取下一个表项,然后继续读入下一个簇的数据,直到返回的下一表项为0xfff为止,表示loader.bin完全加载完成
-    mov cl, 1
-    call Func_ReadOneSector
-    pop ax
-    call Func_GetFATEntry
-    cmp ax, 0xfff
-    jz Label_File_Loaded
-    push ax
-    mov dx, RootDirSectors
-    add ax, dx
-    add ax, SectorBalance
-    add bx, [BPB_BytesPerSec]
-    jmp Label_Go_On_Loading_File
-
-Label_File_Loaded:
-    ; 跳转到loader
-    ; 这个指令结束后,目标段会复制到CS寄存器中
-    jmp BaseOfLoader:OffsetOfLoader
-
-
-; 从软盘读取一个扇区
-; AX=待读取的磁盘起始扇区号
-; CL=读入的扇区数量
-; ES:BX=>目标缓冲区起始地址
-Func_ReadOneSector:
-    push bp
-    mov bp,  sp
-    sub esp, 2
-    mov byte [bp-2], cl
-    push bx
-    mov bl, [BPB_SecPerTrk]
-    div bl  ;用AX寄存器中的值除以BL,得到目标磁道号(商:AL)以及目标磁道内的起始扇区号(余数:AH)
-    inc ah  ; 由于磁道内的起始扇区号从1开始计数,因此将余数+1
-    mov cl, ah
-    mov dh, al
-    shr al, 1 ;计算出柱面号
-    mov ch, al
-    and dh, 1;计算出磁头号
-
-    pop bx
-    mov dl, [BS_DrvNum]
-    ;最终,dh存储了磁头号,dl存储驱动器号
-    ;   ch存储柱面号,cl存储起始扇区号
-
-Label_Go_On_Reading:
-    ; 使用BIOS中断服务程序INT13h的主功能号AH=02h实现软盘读取操作
-    mov ah, 2
-    mov al, byte [bp-2]
-    int 0x13
-    jc Label_Go_On_Reading  ;当CF标志位被复位时,说明数据读取完成,恢复调用现场
-
-    add esp, 2
-    pop bp
-    ret
-
-
-;   解析FAT表项,根据当前FAT表项索引出下一个FAT表项
-Func_GetFATEntry:
-    ; AX=FAT表项号(输入、输出参数)
-    ; 保存将要被修改的寄存器
-    push es
-    push bx
-    push ax
-
-    ; 扩展段寄存器
-    mov ax, 00
-    mov es, ax
-
-    pop ax
-    mov byte [Odd], 0   ;将奇数标志位置0
-
-    ; 将FAT表项号转换为总的字节号
-    mov bx, 3
-    mul bx
-    mov bx, 2
-    div bx
-
-    cmp dx, 0
-    jz Label_Even ; 偶数项
-    mov byte [Odd], 1
-
-Label_Even:
-    xor dx, dx  ;把dx置0
-
-    ; 计算得到扇区号(商)和扇区内偏移(余数)
-    mov bx, [BPB_BytesPerSec]
-    div bx
-    push dx
-
-    ; 读取两个扇区到[es:bx]
-    mov bx, 0x8000
-    add ax, SectorNumOfFAT1Start
-    mov cl, 2 ; 设置读取两个扇区,解决FAT表项跨扇区的问题
-    call Func_ReadOneSector
-
-    pop dx
-    add bx, dx
-    mov ax, [es:bx]
-    cmp byte [Odd], 1
-    jnz Label_Even_2 ;若是偶数项,则跳转
-
-    shr ax, 4 ; 解决奇偶项错位问题
-
-Label_Even_2:
-    and ax, 0x0fff ; 确保表项号在正确的范围内  0x0003~0x0fff
-    pop bx
-    pop es
-    ret
-
-
-
-; 临时变量
-RootDirSizeForLoop dw RootDirSectors
-SectorNo dw 0
-Odd db 0
-
-
-; 显示的文本
-StartBootMessage:   db  "[DragonOS] Start Booting"
-NoLoaderMessage: db "[ERROR] No LOADER Found."
-LoaderFileName:		db	"LOADER  BIN",0 ;最后这个0是为了填满12字节的宽度
-
-;填满整个扇区的512字节
-    times 510 - ( $ - $$ ) db 0
-    dw 0xaa55 ;===确保以0x55 0xaa为结尾
-

+ 0 - 25
bootloader/fat12.inc

@@ -1,25 +0,0 @@
-; === 这是FAT12文件系统的信息 ===
-RootDirSectors equ 14   ;根目录占用的扇区数
-SectorNumOfRootDirStart equ 19  ; 根目录的起始扇区号
-SectorNumOfFAT1Start equ 1  ; FAT1表的起始扇区号 (因为前面有一个保留扇区(引导扇区))
-SectorBalance equ 17    ;平衡文件/目录的起始簇号与数据区域的起始簇号的差值。
-
-    BS_OEMName  db  'DragonOS'
-    BPB_BytesPerSec dw 512
-    BPB_SecPerClus db 1
-    BPB_RsvdSecCnt  dw  1
-    BPB_NumFATs db 2
-    BPB_RootEntCnt dw 224
-    BPB_TotSec16 dw 2880
-    BPB_Media db 0xf0
-    BPB_FATSz16 dw 9
-    BPB_SecPerTrk dw 18
-    BPB_NumHeads dw 2
-    BPB_HiddSec dd 0
-    BPB_TotSec32 dd 0
-    BS_DrvNum db 0
-    BS_Reserved1 db 0
-    BS_BootSig db 0x29
-    BS_VolID dd 0
-    BS_VolLab db 'boot loader'
-    BS_FileSysType db 'FAT12   '

+ 0 - 846
bootloader/loader.asm

@@ -1,846 +0,0 @@
-; |==================|
-; |    这是loader程序  |
-; |==================|
-; Created by longjin, 2022/01/17
-
-; 由于实模式下,物理地址为CS<<4+IP,而从boot的定义中直到,loader的CS为0x1000, 因此loader首地址为0x10000
-org 0x10000
-    jmp Label_Start
-
-%include 'fat12.inc'    ; 将fat12文件系统的信息包含进来
-
-Base_Of_Kernel_File equ 0x00
-Offset_Of_Kernel_File equ 0x100000 ; 设置内核文件的地址空间从1MB处开始。(大于实模式的寻址空间)
-
-Base_Tmp_Of_Kernel_Addr equ 0x00
-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 ; 代码段和数据段的段基地址都设置在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位宽的处理器上
-
-Label_Start:
-    mov ax, cs
-    mov ds, ax ; 初始化数据段寄存器
-    mov es, ax ; 初始化附加段寄存器
-    mov ax, 0x00
-    mov ss, ax ;初始化堆栈段寄存器
-    mov sp, 0x7c00
-
-    ;在屏幕上显示 start Loader
-    mov ax, 0x1301
-    mov bx, 0x000f
-    mov dx, 0x0100  ;在第2行显示
-    mov cx, 26 ;设置消息长度
-    push ax
-
-    mov ax, ds
-    mov es, ax
-    pop ax
-    mov bp, Message_Start_Loader
-    int 0x10
-    ;jmp $
-
-    ; 使用A20快速门来开启A20信号线
-    push ax
-    in al, 0x92 ; A20快速门使用I/O端口0x92来处理A20信号线
-    or al, 0x02 ; 通过将0x92端口的第1位置1,开启A20地址线
-    out 0x92, al
-    pop ax
-
-    cli ; 关闭外部中断
-
-    db 0x66
-    lgdt [GdtPtr]   ; LGDT/LIDT - 加载全局/中断描述符表格寄存器
-
-    ; 置位CR0寄存器的第0位,开启保护模式
-    mov eax, cr0
-    or eax, 1
-    mov cr0, eax
-
-    ; 为fs寄存器加载新的数据段的值
-    mov ax, SelectorData32
-    mov fs, ax
-
-    ; fs寄存器加载完成后,立即从保护模式退出。 这样能使得fs寄存器在实模式下获得大于1MB的寻址能力。
-    mov eax, cr0
-    and al, 11111110b ; 将第0位置0
-    mov cr0, eax
-
-    sti ; 开启外部中断
-
-
-
-
-; =========在文件系统中搜索 kernel.bin==========
-    mov word [SectorNo], SectorNumOfRootDirStart    ;保存根目录起始扇区号
-
-Label_Search_In_Root_Dir_Begin:
-    cmp word [RootDirSizeForLoop],  0 ; 比较根目录扇区数量和0的关系。 cmp实际上是进行了一个减法运算
-    jz Label_No_KernelBin ; 等于0,不存在kernel.bin
-    dec word [RootDirSizeForLoop]
-
-    mov ax, 0x00
-    mov es, ax
-    mov bx, 0x8000
-    mov ax, [SectorNo]  ;向函数传入扇区号
-    mov cl, 1
-    call Func_ReadOneSector
-    mov si, Kernel_FileName ;向源变址寄存器传入Loader文件的名字
-    mov di, 0x8000
-    cld ;由于LODSB的加载方向与DF标志位有关,因此需要用CLD清零DF标志位
-
-    mov dx, 0x10 ; 每个扇区的目录项的最大条数是(512/32=16,也就是0x10)
-
-Label_Search_For_LoaderBin:
-    cmp dx, 0
-    jz Label_Goto_Next_Sector_In_Root_Dir
-    dec dx
-    mov cx, 11 ; cx寄存器存储目录项的文件名长度, 11B,包括了文件名和扩展名,但是不包括 分隔符'.'
-
-Label_Cmp_FileName:
-    cmp cx, 0
-    jz Label_FileName_Found
-    dec cx
-    lodsb ; 把si对应的字节载入al寄存器中,然后,由于DF为0,si寄存器自增
-    cmp al, byte [es:di]    ; 间接取址[es+di]。   也就是进行比较当前文件的名字对应字节和loader文件名对应字节
-    jz  Label_Go_On     ; 对应字节相同
-    jmp Label_Different     ; 字节不同,不是同一个文件
-
-Label_Go_On:
-    inc di
-    jmp Label_Cmp_FileName
-
-Label_Different:
-    and di, 0xffe0 ;将di恢复到当前目录项的第0字节
-    add di, 0x20     ;将di跳转到下一目录项的第0字节
-    mov si, Kernel_FileName
-    jmp Label_Search_For_LoaderBin  ;继续搜索下一目录项
-
-Label_Goto_Next_Sector_In_Root_Dir:
-    add word [SectorNo], 1
-    jmp Label_Search_In_Root_Dir_Begin
-
-Label_No_KernelBin:
-    ; 在屏幕上显示 [ERROR] No Kernel Found.
-    mov ax, 0x1301
-    mov bx, 0x000c  ; 红色闪烁高亮黑底
-    mov dx, 0x0200  ; 显示在第3行(前面已经显示过2行了)
-    mov cx, 24  ; 字符串长度
-    push ax
-    mov ax, ds
-    mov es, ax
-    pop ax
-    mov bp, Message_No_Loader
-    int 0x10
-
-    jmp $
-
-; ========= 找到了 kernel.bin ===========
-; 将内核加载到内存中
-Label_FileName_Found:
-    mov ax, RootDirSectors
-
-    ; 先取得目录项DIR_FstClus字段的值(起始簇号)
-    and di, 0xffe0
-    add di, 0x1a
-    mov cx, word    [es:di]
-    push cx
-
-
-    add cx, ax
-    add cx, SectorBalance
-    mov eax, Base_Tmp_Of_Kernel_Addr    ; 内核放置的临时地址
-    mov es, eax ;配置es和bx,指定kernel.bin在内存中的起始地址
-    mov bx, Offset_Tmp_Of_Kernel_File
-    mov ax, cx
-
-Label_Go_On_Loading_File:
-    ;push ax
-    ;push bx
-
-    ; 显示字符.
-    ;mov ah, 0x0e
-    ;mov al, "."
-    ;mov bl, 0x0f
-    ;int 0x10
-
-    ;pop bx
-    ;pop ax
-
-
-    ; 读取一个扇区
-    mov cl, 1
-    call Func_ReadOneSector
-    pop ax
-
-    ; ======逐字节将内核程序复制到临时空间,然后转存到内核空间===
-    push cx
-    push eax
-    push fs
-    push edi
-    push ds
-    push esi
-
-    mov cx, 0x0200 ; 指定计数寄存器的值为512, 为后面循环搬运这个扇区的数据做准备
-    mov ax, Base_Of_Kernel_File
-    mov fs, ax ; 这样在物理机上是行不通的,因为这样移动的话,fs就失去了32位寻址能力
-    mov edi, dword  [OffsetOfKernelFileCount]   ; 指定目的变址寄存器
-
-    mov ax, Base_Tmp_Of_Kernel_Addr
-    mov ds, ax
-    mov esi, Offset_Tmp_Of_Kernel_File  ; 指定来源变址寄存器
-
-
-
-Label_Move_Kernel:
-    ; 真正进行数据的移动
-    mov al, byte [ds:esi]   ; 移动到临时区域
-    mov byte [fs:edi], al   ; 再移动到目标区域
-
-    inc esi
-    inc edi
-    loop Label_Move_Kernel
-
-    ; 当前扇区数据移动完毕
-    mov eax, 0x1000
-    mov ds, eax
-
-    mov dword [OffsetOfKernelFileCount],    edi ; 增加偏移量
-
-    pop esi
-    pop ds
-    pop edi
-    pop fs
-    pop eax
-    pop cx
-
-
-    call Func_GetFATEntry
-
-	cmp	ax,	0x0fff
-	jz	Label_File_Loaded
-
-
-
-	push ax
-	mov	dx,	RootDirSectors
-	add	ax,	dx
-	add	ax,	SectorBalance
-
-
-
-    ; 继续读取下一个簇
-	jmp Label_Go_On_Loading_File
-
-Label_File_Loaded:
-
-
-    ;在屏幕上显示 kernel loaded
-    mov ax, 0x1301
-    mov bx, 0x000f
-    mov dx, 0x0200  ;在第3行显示
-    mov cx, 20 ;设置消息长度
-
-    push ax
-
-    mov ax, ds
-    mov es, ax
-    pop ax
-    mov bp, Message_Kernel_Loaded
-    int 0x10
-
-     ; ======直接操作显示内存=======
-    ; 从内存的0x0B800开始,是一段用于显示字符的内存空间。
-    ; 每个字符占用2bytes,低字节保存要显示的字符,高字节保存样式
-    mov ax, 0xB800
-    mov gs, ax
-    mov ah, 0x0F ;黑底白字
-    mov al, '.'
-    mov [gs:((80 * 2 + 20) * 2)], ax ;在屏幕第0行,39列
-
-
-
-
-
-Label_Kill_Motor:
-    ; =====关闭软驱的马达======
-    ; 向IO端口0x03f2写入0,关闭所有软驱
-    push dx
-    mov dx, 0x03F2
-    mov al, 0
-    out dx, al
-    pop dx
-
-    ; =====获取物理地址空间====
-
-    ; 显示 正在获取内存结构
-    mov ax, 0x1301
-    mov bx, 0x000F
-    mov dx, 0x0300 ; 在第四行显示
-    mov cx, 34
-    push ax
-    mov ax, ds
-    mov es, ax
-    pop ax
-    mov bp, Message_Start_Get_Mem_Struct
-    int 0x10
-
-
-    mov ax, 0x00
-    mov es, ax
-
-    mov di, Memory_Struct_Buffer_Addr ; 设置内存结构信息存储的地址
-    mov ebx, 0 ;第一次调用0x15的时候,ebx要置为0 ebx存储的是下一个待返回的ARDS  (Address Range Descriptor Structure)
-
-Label_Get_Mem_Struct:
-    ;==== 获取内存物理地址信息
-    ; 使用0x15中断程序的功能号0xe820来获取内存信息
-    ; 返回信息在[es:di]指向的内存中
-    ; 一共要分5次才能把20个字节的信息获取完成
-    ; 这些信息在内核初始化内存管理单元的时候,会去解析它们。
-
-    mov eax, 0xe820
-    mov ecx, 20 ; 指定ARDS结构的大小,是固定值20
-    mov edx, 0x534d4150 ; 固定签名标记,是字符串“SMAP”的ASCII码
-    int 0x15
-
-    jc Label_Get_Mem_Fail ; 若调用出错,则CF=1
-
-    add di, 20
-    cmp ebx, 0
-    jne Label_Get_Mem_Struct ; ebx不为0
-    jmp Label_Get_Mem_OK ; 获取内存信息完成
-
-Label_Get_Mem_Fail:
-    ; =====获取内存信息失败====
-    ; 显示 正在获取内存结构
-    mov ax, 0x1301
-    mov bx, 0x000c
-    mov dx, 0x0400 ; 在第5行显示
-    mov cx, 33
-    push ax
-    mov ax, ds
-    mov es, ax
-    pop ax
-    mov bp, Message_Get_Mem_Failed
-    int 0x10
-
-    jmp $
-
-Label_Get_Mem_OK:
-    ; ==== 成功获取内存信息 ===
-    mov ax, 0x1301
-    mov bx, 0x000f
-    mov dx, 0x0400 ; 在第5行显示
-    mov cx, 38
-    push ax
-    mov ax, ds
-    mov es, ax
-    pop ax
-    mov bp, Message_Get_Mem_Success
-    int 0x10
-
-    jmp Label_Get_SVGA_Info
-
-Label_Get_SVGA_Info:
-    ; ==== 获取SVGA芯片的信息
-    mov ax, 0x1301
-    mov bx, 0x000f
-    mov dx, 0x0500 ; 在第6行显示
-    mov cx, 34
-    push ax
-    mov ax, ds
-    mov es, ax
-    pop ax
-    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 ; 启用保护模式
-    or eax, 0x22 ; 启用x87浮点运算单元
-    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
-
-    ;now enable SSE and the like
-    mov eax, cr0
-    and ax, 0xFFFB		;clear coprocessor emulation CR0.EM
-    or ax, 0x2			;set coprocessor monitoring  CR0.MP
-    mov cr0, eax
-    mov eax, cr4
-    or ax, 3 << 9		;set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
-    mov cr4, 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=读入的扇区数量
-; ES:BX=>目标缓冲区起始地址
-Func_ReadOneSector:
-
-    push bp
-    mov bp,  sp
-    sub esp, 2
-    mov byte [bp-2], cl
-    push bx
-    mov bl, [BPB_SecPerTrk]
-    div bl  ;用AX寄存器中的值除以BL,得到目标磁道号(商:AL)以及目标磁道内的起始扇区号(余数:AH)
-    inc ah  ; 由于磁道内的起始扇区号从1开始计数,因此将余数+1
-    mov cl, ah
-    mov dh, al
-    shr al, 1 ;计算出柱面号
-    mov ch, al
-    and dh, 1;计算出磁头号
-
-    pop bx
-    mov dl, [BS_DrvNum]
-    ;最终,dh存储了磁头号,dl存储驱动器号
-    ;   ch存储柱面号,cl存储起始扇区号
-
-Label_Go_On_Reading:
-    ; 使用BIOS中断服务程序INT13h的主功能号AH=02h实现软盘读取操作
-    mov ah, 2
-    mov al, byte [bp-2]
-    int 0x13
-
-    jc Label_Go_On_Reading  ;当CF标志位被复位时,说明数据读取完成,恢复调用现场
-
-    add esp, 2
-    pop bp
-
-    ret
-
-
-;   解析FAT表项,根据当前FAT表项索引出下一个FAT表项
-Func_GetFATEntry:
-    ; AX=FAT表项号(输入、输出参数)
-    ; 保存将要被修改的寄存器
-
-
-    push es
-    push bx
-    push ax
-
-    ; 扩展段寄存器
-    mov ax, 00
-    mov es, ax
-
-    pop ax
-    mov byte [Odd], 0   ;将奇数标志位置0
-
-    ; 将FAT表项号转换为总的字节号
-    mov bx, 3
-    mul bx
-    mov bx, 2
-    div bx
-
-    cmp dx, 0
-    jz Label_Even ; 偶数项
-    mov byte [Odd], 1
-
-Label_Even:
-    xor dx, dx  ;把dx置0
-
-    ; 计算得到扇区号(商)和扇区内偏移(余数)
-    mov bx, [BPB_BytesPerSec]
-    div bx
-    push dx
-
-    ; 读取两个扇区到[es:bx]
-    mov bx, 0x8000
-    add ax, SectorNumOfFAT1Start
-    mov cl, 2 ; 设置读取两个扇区,解决FAT表项跨扇区的问题
-
-
-
-    call Func_ReadOneSector
-
-    pop dx
-    add bx, dx
-    mov ax, [es:bx]
-    cmp byte [Odd], 1
-    jnz Label_Even_2 ;若是偶数项,则跳转
-
-    shr ax, 4 ; 解决奇偶项错位问题
-
-Label_Even_2:
-    and ax, 0x0fff ; 确保表项号在正确的范围内  0x0003~0x0fff
-    pop bx
-    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] 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

+ 8 - 3
kernel/Makefile

@@ -7,11 +7,13 @@ DIR_LIB=lib
 lib_patterns := *.a
 LIB_FILES := $(foreach DIR,$(DIR_LIB),$(addprefix $(DIR)/,$(lib_patterns)))
 all: kernel
-	objcopy -I elf64-x86-64 -S -R  ".eh_frame" -R ".comment" -O binary kernel ../bin/kernel/kernel.bin
+#objcopy -I elf64-x86-64 -S -R ".comment" -O elf64-x86-64 kernel ../bin/kernel/kernel.elf
+	cp kernel ../bin/kernel/kernel.elf
 
 
-kernel: head.o entry.o main.o printk.o trap.o mm.o irq.o 8259A.o process.o syscall.o
-	ld -b elf64-x86-64 -z muldefs -o kernel head.o exception/entry.o main.o common/printk.o exception/trap.o exception/irq.o exception/8259A.o mm/mm.o process/process.o syscall/syscall.o -T link.lds
+kernel: head.o entry.o main.o printk.o trap.o mm.o irq.o 8259A.o process.o syscall.o multiboot2.o
+	ld -b elf64-x86-64 -z muldefs -o kernel head.o exception/entry.o main.o common/printk.o exception/trap.o exception/irq.o exception/8259A.o mm/mm.o process/process.o syscall/syscall.o driver/multiboot2/multiboot2.o \
+	-T link.lds
 
 head.o: head.S
 	gcc -E head.S > head.s # 预处理
@@ -49,5 +51,8 @@ process.o: process/process.c
 syscall.o: syscall/syscall.c
 	gcc -mcmodel=large -fno-builtin -m64 -c syscall/syscall.c -o syscall/syscall.o
 
+multiboot2.o: driver/multiboot2/multiboot2.c 
+	gcc -mcmodel=large -fno-builtin -m64 -c driver/multiboot2/multiboot2.c  -o driver/multiboot2/multiboot2.o
+
 clean: 
 	rm -rf $(GARBAGE)

+ 65 - 0
kernel/common/boot_info.h

@@ -0,0 +1,65 @@
+
+/**
+ * @file boot_info.h
+ * @brief 启动信息接口
+ * @author Zone.N ([email protected])
+ * @version 1.0
+ * @date 2021-09-18
+ * @copyright MIT LICENSE
+ * https://github.com/Simple-XX/SimpleKernel
+ * @par change log:
+ * <table>
+ * <tr><th>Date<th>Author<th>Description
+ * <tr><td>2021-09-18<td>digmouse233<td>迁移到 doxygen
+ * </table>
+ */
+
+#ifndef _BOOT_INFO_H_
+#define _BOOT_INFO_H_
+
+#include "stdint.h"
+//#include "resource.h"
+
+/**
+ * @brief 启动信息接口
+ * 由引导传递的机器信息处理
+ * 如 grub2 传递的 multiboot2 结构
+ * opensbi 传递的 dtb 结构
+ * 注意这部分是通过内存传递的,在重新保存之前不能被覆盖
+ * 架构专有的数据在 dtb.h 或 multiboot2.h
+ * 实现在 dtb.cpp 或 multiboot2.cpp
+ */
+namespace BOOT_INFO {
+    /// 声明,定义在具体的实现中
+    /// 地址
+    extern "C" uintptr_t boot_info_addr;
+    /// 长度
+    extern size_t boot_info_size;
+
+    /**
+     * @brief 初始化,定义在具体实现中
+     * @return true            成功
+     * @return false           成功
+     */
+    extern bool init(void);
+
+    /**
+     * @brief 获取物理内存信息
+     * @return resource_t      物理内存资源信息
+     */
+    extern resource_t get_memory(void);
+
+    /**
+     * @brief 获取 clint 信息
+     * @return resource_t       clint 资源信息
+     */
+    extern resource_t get_clint(void);
+    
+    /**
+     * @brief 获取 plic 信息
+     * @return resource_t       plic 资源信息
+     */
+    extern resource_t get_plic(void);
+};
+
+#endif /* _BOOT_INFO_H_ */

+ 225 - 0
kernel/driver/multiboot2/boot.S

@@ -0,0 +1,225 @@
+
+// 以下是来自 multiboot2 规范的定义
+//  How many bytes from the start of the file we search for the header.
+#define MULTIBOOT_SEARCH 32768
+#define MULTIBOOT_HEADER_ALIGN 8
+
+//  The magic field should contain this.
+#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
+
+//  This should be in %eax.
+#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
+
+//  Alignment of multiboot modules.
+#define MULTIBOOT_MOD_ALIGN 0x00001000
+
+//  Alignment of the multiboot info structure.
+#define MULTIBOOT_INFO_ALIGN 0x00000008
+
+//  Flags set in the 'flags' member of the multiboot header.
+
+#define MULTIBOOT_TAG_ALIGN 8
+#define MULTIBOOT_TAG_TYPE_END 0
+#define MULTIBOOT_TAG_TYPE_CMDLINE 1
+#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
+#define MULTIBOOT_TAG_TYPE_MODULE 3
+#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
+#define MULTIBOOT_TAG_TYPE_BOOTDEV 5
+#define MULTIBOOT_TAG_TYPE_MMAP 6
+#define MULTIBOOT_TAG_TYPE_VBE 7
+#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
+#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
+#define MULTIBOOT_TAG_TYPE_APM 10
+#define MULTIBOOT_TAG_TYPE_EFI32 11
+#define MULTIBOOT_TAG_TYPE_EFI64 12
+#define MULTIBOOT_TAG_TYPE_SMBIOS 13
+#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
+#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
+#define MULTIBOOT_TAG_TYPE_NETWORK 16
+#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
+#define MULTIBOOT_TAG_TYPE_EFI_BS 18
+#define MULTIBOOT_TAG_TYPE_EFI32_IH 19
+#define MULTIBOOT_TAG_TYPE_EFI64_IH 20
+#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
+
+#define MULTIBOOT_HEADER_TAG_END 0
+#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
+#define MULTIBOOT_HEADER_TAG_ADDRESS 2
+#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
+#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
+#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
+#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
+#define MULTIBOOT_HEADER_TAG_EFI_BS 7
+#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
+#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
+#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
+
+#define MULTIBOOT_ARCHITECTURE_I386 0
+#define MULTIBOOT_ARCHITECTURE_MIPS32 4
+#define MULTIBOOT_HEADER_TAG_OPTIONAL 1
+
+#define MULTIBOOT_LOAD_PREFERENCE_NONE 0
+#define MULTIBOOT_LOAD_PREFERENCE_LOW 1
+#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
+
+#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
+#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
+
+
+// 直接用 -m64 编译出来的是 64 位代码,
+// 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。
+// 得加一层跳转到 64 位的 -m32 代码,开启 long 模式后再跳转到以 -m64 编译的代码中
+// 对于 x86_64,需要在启动阶段进入长模式(IA32E),这意味着需要一个临时页表
+// See https://wiki.osdev.org/Creating_a_64-bit_kernel: 
+// With a 32-bit bootstrap in your kernel
+
+// 这部分是从保护模式启动 long 模式的代码
+// 工作在 32bit
+// 声明这一段代码以 32 位模式编译
+.code32
+
+// multiboot2 文件头
+// 计算头长度
+.SET HEADER_LENGTH, multiboot_header_end - multiboot_header
+// 计算校验和
+.SET CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + HEADER_LENGTH)
+// 8 字节对齐
+.align MULTIBOOT_HEADER_ALIGN
+// 声明所属段
+.section .multiboot_header
+multiboot_header:
+    // 魔数
+    .long MULTIBOOT2_HEADER_MAGIC
+    // 架构
+    .long MULTIBOOT_ARCHITECTURE_I386
+    // 头长度
+    .long HEADER_LENGTH
+    // 校验和
+    .long CHECKSUM
+    // 添加其它内容在此,详细信息见 Multiboot2 Specification version 2.0.pdf
+	.short MULTIBOOT_HEADER_TAG_END
+    // 结束标记
+    .short 0
+    .long 8
+multiboot_header_end:
+
+// 临时页表 4KB/页
+.section .data
+.align 0x1000
+pml4:
+    .skip 0x1000
+pdpt:
+    .skip 0x1000
+pd:
+    .skip 0x1000
+pt:
+    .skip 0x1000
+
+// 临时 GDT
+.align 16
+gdt64:
+null_desc:
+    .short 0xFFFF
+    .short 0
+    .byte 0
+    .byte 0
+    .byte 0
+    .byte 0
+code_desc:
+    .short 0
+    .short 0
+    .byte 0
+    .byte 0x9A
+    .byte 0x20
+    .byte 0
+data_desc:
+    .short 0
+    .short 0
+    .byte 0
+    .byte 0x92
+    .byte 0
+    .byte 0
+user_code_desc:
+    .short 0
+    .short 0
+    .byte 0
+    .byte 0xFA
+    .byte 0x20
+    .byte 0
+user_data_desc:
+    .short 0
+    .short 0
+    .byte 0
+    .byte 0xF2
+    .byte 0
+    .byte 0
+gdt64_pointer:
+    .short gdt64_pointer-gdt64-1
+    .quad gdt64
+gdt64_pointer64:
+    .short gdt64_pointer-gdt64-1
+    .quad gdt64
+
+.section .text
+.global _start
+.type _start, @function
+# 在 multiboot2.cpp 中定义
+.extern boot_info_addr
+.extern multiboot2_magic
+_start:
+    // 关中断
+    cli
+    // multiboot2_info 结构体指针
+    //mov %ebx, boot_info_addr
+    // 魔数
+   // mov %eax, multiboot2_magic
+    // 从保护模式跳转到长模式
+    // 1. 允许 PAE
+    mov %cr4, %eax
+    or $(1<<5), %eax
+    mov %eax, %cr4
+    // 2. 设置临时页表
+    // 最高级
+    mov $pml4, %eax
+    mov $pdpt, %ebx
+    or $0x3, %ebx
+    mov %ebx, 0(%eax)
+    // 次级
+    mov $pdpt, %eax
+    mov $pd, %ebx
+    or $0x3, %ebx
+    mov %ebx, 0(%eax)
+    // 次低级
+    mov $pd, %eax
+    mov $pt, %ebx
+    or $0x3, %ebx
+    mov %ebx, 0(%eax)
+    // 最低级
+    // 循环 512 次,填满一页
+    mov $512, %ecx
+    mov $pt, %eax
+    mov $0x3, %ebx
+.fill_pt:
+    mov %ebx, 0(%eax)
+    add $0x1000, %ebx
+    add $8, %eax
+    loop .fill_pt
+    // 填写 CR3
+    mov $pml4, %eax
+    mov %eax, %cr3
+    // 3. 切换到 long 模式
+    mov $0xC0000080, %ecx
+    rdmsr
+    or $(1<<8), %eax
+    wrmsr
+    // 4. 开启分页
+    mov %cr0, %eax
+    or $(1<<31), %eax
+    mov %eax, %cr0
+    // 5. 重新设置 GDT
+    mov $gdt64_pointer, %eax
+    lgdt 0(%eax)
+    // 6. 跳转到 64 位代码执行
+    jmp $0x8, $_start64
+    hlt
+    ret

+ 3 - 0
kernel/driver/multiboot2/multiboot2.c

@@ -0,0 +1,3 @@
+
+int *boot_info_addr;
+int *multiboot2_magic;

+ 104 - 0
kernel/driver/multiboot2/multiboot2.cpp

@@ -0,0 +1,104 @@
+/**
+ * @file multiboot2.cpp
+ * @brief multiboot2 解析实现
+ * @author Zone.N ([email protected])
+ * @version 1.0
+ * @date 2021-09-18
+ * @copyright MIT LICENSE
+ * https://github.com/Simple-XX/SimpleKernel
+ * @par change log:
+ * <table>
+ * <tr><th>Date<th>Author<th>Description
+ * <tr><td>2021-09-18<td>digmouse233<td>迁移到 doxygen
+ * </table>
+ */
+
+#include "assert.h"
+#include "stdio.h"
+//#include "common.h"
+#include "multiboot2.h"
+#include "boot_info.h"
+//#include "resource.h"
+//#include "pmm.h"
+
+/// @todo 优化
+void MULTIBOOT2::multiboot2_iter(bool (*_fun)(const iter_data_t *, void *),
+                                 void *_data) {
+    uintptr_t addr = BOOT_INFO::boot_info_addr;
+    // 下一字节开始为 tag 信息
+    iter_data_t *tag = (iter_data_t *)(addr + 8);
+    for (; tag->type != MULTIBOOT_TAG_TYPE_END;
+         tag = (iter_data_t *)((uint8_t *)tag + COMMON::ALIGN(tag->size, 8))) {
+        if (_fun(tag, _data) == true) {
+            return;
+        }
+    }
+    return;
+}
+
+bool MULTIBOOT2::multiboot2_init(void) {
+    uintptr_t addr = BOOT_INFO::boot_info_addr;
+    // 判断魔数是否正确
+    assert(BOOT_INFO::multiboot2_magic == MULTIBOOT2_BOOTLOADER_MAGIC);
+    assert((reinterpret_cast<uintptr_t>(addr) & 7) == 0);
+    // addr+0 保存大小
+    BOOT_INFO::boot_info_size = *(uint32_t *)addr;
+    return true;
+}
+
+// 读取 grub2 传递的物理内存信息,保存到 e820map_t 结构体中
+// 一般而言是这样的
+// 地址(长度) 类型
+// 0x00(0x9F000) 0x1
+// 0x9F000(0x1000) 0x2
+// 0xE8000(0x18000) 0x2
+// 0x100000(0x7EF0000) 0x1
+// 0x7FF0000(0x10000) 0x3
+// 0xFFFC0000(0x40000) 0x2
+bool MULTIBOOT2::get_memory(const iter_data_t *_iter_data, void *_data) {
+    if (_iter_data->type != MULTIBOOT2::MULTIBOOT_TAG_TYPE_MMAP) {
+        return false;
+    }
+    resource_t *resource = (resource_t *)_data;
+    resource->type       = resource_t::MEM;
+    resource->name       = (char *)"available phy memory";
+    resource->mem.addr   = 0x0;
+    resource->mem.len    = 0;
+    MULTIBOOT2::multiboot_mmap_entry_t *mmap =
+        ((MULTIBOOT2::multiboot_tag_mmap_t *)_iter_data)->entries;
+    for (; (uint8_t *)mmap < (uint8_t *)_iter_data + _iter_data->size;
+         mmap = (MULTIBOOT2::multiboot_mmap_entry_t
+                     *)((uint8_t *)mmap +
+                        ((MULTIBOOT2::multiboot_tag_mmap_t *)_iter_data)
+                            ->entry_size)) {
+        // 如果是可用内存或地址小于 1M
+        // 这里将 0~1M 的空间全部算为可用,在 c++ 库可用后进行优化
+        if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE ||
+            mmap->addr < 1 * COMMON::MB) {
+            // 长度+
+            resource->mem.len += mmap->len;
+        }
+    }
+    return true;
+}
+
+namespace BOOT_INFO {
+    // 地址
+    uintptr_t boot_info_addr;
+    // 长度
+    size_t boot_info_size;
+    // 魔数
+    uint32_t multiboot2_magic;
+
+    bool init(void) {
+        auto res = MULTIBOOT2::multiboot2_init();
+        info("BOOT_INFO init.\n");
+        return res;
+    }
+
+    resource_t get_memory(void) {
+        resource_t resource;
+        MULTIBOOT2::multiboot2_iter(MULTIBOOT2::get_memory, &resource);
+        return resource;
+    }
+};

+ 334 - 0
kernel/driver/multiboot2/multiboot2.h

@@ -0,0 +1,334 @@
+/**
+ * @file multiboot2.h
+ * @brief multiboot2 解析
+ * @author Zone.N ([email protected])
+ * @version 1.0
+ * @date 2021-09-18
+ * @copyright MIT LICENSE
+ * https://github.com/Simple-XX/SimpleKernel
+ * @par change log:
+ * <table>
+ * <tr><th>Date<th>Author<th>Description
+ * <tr><td>2021-09-18<td>digmouse233<td>迁移到 doxygen
+ * </table>
+ */
+
+#ifndef _MULTIBOOT2_H_
+#define _MULTIBOOT2_H_
+
+#include "stdint.h"
+#include "stdbool.h"
+#include "boot_info.h"
+
+/// @see Multiboot2 Specification version 2.0.pdf
+// 启动后,在 32 位内核进入点,机器状态如下:
+//   1. CS 指向基地址为 0x00000000,限长为4G – 1的代码段描述符。
+//   2. DS,SS,ES,FS 和 GS 指向基地址为0x00000000,限长为4G –
+//   1的数据段描述符。
+//   3. A20 地址线已经打开。
+//   4. 页机制被禁止。
+//   5. 中断被禁止。
+//   6. EAX = 0x2BADB002
+//   7. 系统信息和启动信息块的线性地址保存在 EBX中(相当于一个指针)。
+//      以下即为这个信息块的结构
+
+/**
+ * @brief MULTIBOOT2 接口抽象
+ */
+class MULTIBOOT2 {
+private:
+    /*  How many bytes from the start of the file we search for the header. */
+    static constexpr const uint32_t MULTIBOOT_SEARCH       = 32768;
+    static constexpr const uint32_t MULTIBOOT_HEADER_ALIGN = 8;
+
+    /*  The magic field should contain this. */
+    static constexpr const uint32_t MULTIBOOT2_HEADER_MAGIC = 0xe85250d6;
+
+    /*  This should be in %eax. */
+    static constexpr const uint32_t MULTIBOOT2_BOOTLOADER_MAGIC = 0x36d76289;
+
+    /*  Alignment of multiboot modules. */
+    static constexpr const uint32_t MULTIBOOT_MOD_ALIGN = 0x00001000;
+
+    /*  Alignment of the multiboot info structure. */
+    static constexpr const uint32_t MULTIBOOT_INFO_ALIGN = 0x00000008;
+
+    /*  Flags set in the 'flags' member of the multiboot header. */
+
+    static constexpr const uint32_t MULTIBOOT_TAG_ALIGN                 = 8;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_END              = 0;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_CMDLINE          = 1;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME = 2;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_MODULE           = 3;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_BASIC_MEMINFO    = 4;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_BOOTDEV          = 5;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_MMAP             = 6;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_VBE              = 7;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_FRAMEBUFFER      = 8;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_ELF_SECTIONS     = 9;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_APM              = 10;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_EFI32            = 11;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_EFI64            = 12;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_SMBIOS           = 13;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_ACPI_OLD         = 14;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_ACPI_NEW         = 15;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_NETWORK          = 16;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_EFI_MMAP         = 17;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_EFI_BS           = 18;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_EFI32_IH         = 19;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_EFI64_IH         = 20;
+    static constexpr const uint32_t MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR   = 21;
+
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_END = 0;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST =
+        1;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_ADDRESS       = 2;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS = 3;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS = 4;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_FRAMEBUFFER   = 5;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_MODULE_ALIGN  = 6;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_EFI_BS        = 7;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 =
+        8;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 =
+        9;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_RELOCATABLE = 10;
+
+    static constexpr const uint32_t MULTIBOOT_ARCHITECTURE_I386   = 0;
+    static constexpr const uint32_t MULTIBOOT_ARCHITECTURE_MIPS32 = 4;
+    static constexpr const uint32_t MULTIBOOT_HEADER_TAG_OPTIONAL = 1;
+
+    static constexpr const uint32_t MULTIBOOT_LOAD_PREFERENCE_NONE = 0;
+    static constexpr const uint32_t MULTIBOOT_LOAD_PREFERENCE_LOW  = 1;
+    static constexpr const uint32_t MULTIBOOT_LOAD_PREFERENCE_HIGH = 2;
+
+    static constexpr const uint32_t MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED =
+        1;
+    static constexpr const uint32_t MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED =
+        2;
+
+    struct multiboot_header_t {
+        // Must be MULTIBOOT_MAGIC - see above.
+        uint32_t magic;
+        // ISA
+        uint32_t architecture;
+        // Total header length.
+        uint32_t header_length;
+        // The above fields plus this one must equal 0 mod 2^32.
+        uint32_t checksum;
+    };
+
+    struct multiboot_header_tag_t {
+        uint16_t type;
+        uint16_t flags;
+        uint32_t size;
+    };
+
+    struct multiboot_header_tag_information_request_t : multiboot_header_tag_t {
+        uint32_t requests[0];
+    };
+
+    struct multiboot_header_tag_address_t : multiboot_header_tag_t {
+        uint32_t header_addr;
+        uint32_t load_addr;
+        uint32_t load_end_addr;
+        uint32_t bss_end_addr;
+    };
+
+    struct multiboot_header_tag_entry_address_t : multiboot_header_tag_t {
+        uint32_t entry_addr;
+    };
+
+    struct multiboot_header_tag_console_flags_t : multiboot_header_tag_t {
+        uint32_t console_flags;
+    };
+
+    struct multiboot_header_tag_framebuffer_t : multiboot_header_tag_t {
+        uint32_t width;
+        uint32_t height;
+        uint32_t depth;
+    };
+
+    struct multiboot_header_tag_module_align_t : multiboot_header_tag_t {
+        ;
+    };
+
+    struct multiboot_header_tag_relocatable_t : multiboot_header_tag_t {
+        uint32_t min_addr;
+        uint32_t max_addr;
+        uint32_t align;
+        uint32_t preference;
+    };
+
+    struct multiboot_color_t {
+        uint8_t red;
+        uint8_t green;
+        uint8_t blue;
+    };
+
+    static constexpr const uint32_t MULTIBOOT_MEMORY_AVAILABLE        = 1;
+    static constexpr const uint32_t MULTIBOOT_MEMORY_RESERVED         = 2;
+    static constexpr const uint32_t MULTIBOOT_MEMORY_ACPI_RECLAIMABLE = 3;
+    static constexpr const uint32_t MULTIBOOT_MEMORY_NVS              = 4;
+    static constexpr const uint32_t MULTIBOOT_MEMORY_BADRAM           = 5;
+    struct multiboot_mmap_entry_t {
+        uint64_t addr;
+        uint64_t len;
+        uint32_t type;
+        uint32_t zero;
+    };
+
+    struct multiboot_tag_t {
+        uint32_t type;
+        uint32_t size;
+    };
+
+    struct multiboot_tag_string_t : multiboot_tag_t {
+        char string[0];
+    };
+
+    struct multiboot_tag_module_t : multiboot_tag_t {
+        uint32_t mod_start;
+        uint32_t mod_end;
+        char     cmdline[0];
+    };
+
+    struct multiboot_tag_basic_meminfo_t : multiboot_tag_t {
+        uint32_t mem_lower;
+        uint32_t mem_upper;
+    };
+
+    struct multiboot_tag_bootdev_t : multiboot_tag_t {
+        uint32_t biosdev;
+        uint32_t slice;
+        uint32_t part;
+    };
+
+    struct multiboot_tag_mmap_t : multiboot_tag_t {
+        uint32_t               entry_size;
+        uint32_t               entry_version;
+        multiboot_mmap_entry_t entries[0];
+    };
+
+    struct multiboot_vbe_info_block_t {
+        uint8_t external_specification[512];
+    };
+
+    struct multiboot_vbe_mode_info_block_t {
+        uint8_t external_specification[256];
+    };
+
+    struct multiboot_tag_vbe_t : multiboot_tag_t {
+        uint16_t vbe_mode;
+        uint16_t vbe_interface_seg;
+        uint16_t vbe_interface_off;
+        uint16_t vbe_interface_len;
+
+        multiboot_vbe_info_block_t      vbe_control_info;
+        multiboot_vbe_mode_info_block_t vbe_mode_info;
+    };
+
+    struct multiboot_tag_elf_sections_t : multiboot_tag_t {
+        uint32_t num;
+        uint32_t entsize;
+        // 段字符串表索引
+        uint32_t shndx;
+        char     sections[0];
+    };
+
+    struct multiboot_tag_apm_t : multiboot_tag_t {
+        uint16_t version;
+        uint16_t cseg;
+        uint32_t offset;
+        uint16_t cseg_16;
+        uint16_t dseg;
+        uint16_t flags;
+        uint16_t cseg_len;
+        uint16_t cseg_16_len;
+        uint16_t dseg_len;
+    };
+
+    struct multiboot_tag_efi32_t : multiboot_tag_t {
+        uint32_t pointer;
+    };
+
+    struct multiboot_tag_efi64_t : multiboot_tag_t {
+        uint64_t pointer;
+    };
+
+    struct multiboot_tag_smbios_t : multiboot_tag_t {
+        uint8_t major;
+        uint8_t minor;
+        uint8_t reserved[6];
+        uint8_t tables[0];
+    };
+
+    struct multiboot_tag_old_acpi_t : multiboot_tag_t {
+        uint8_t rsdp[0];
+    };
+
+    struct multiboot_tag_new_acpi_t : multiboot_tag_t {
+        uint8_t rsdp[0];
+    };
+
+    struct multiboot_tag_network_t : multiboot_tag_t {
+        uint8_t dhcpack[0];
+    };
+
+    struct multiboot_tag_efi_mmap_t : multiboot_tag_t {
+        uint32_t descr_size;
+        uint32_t descr_vers;
+        uint8_t  efi_mmap[0];
+    };
+
+    struct multiboot_tag_efi32_ih_t : multiboot_tag_t {
+        uint32_t pointer;
+    };
+
+    struct multiboot_tag_efi64_ih_t : multiboot_tag_t {
+        uint64_t pointer;
+    };
+
+    struct multiboot_tag_load_base_addr_t : multiboot_tag_t {
+        uint32_t load_base_addr;
+    };
+
+    // 迭代变量
+    // 与 multiboot_tag_t 相同
+    struct iter_data_t {
+        uint32_t type;
+        uint32_t size;
+    };
+
+public:
+    /**
+     * @brief 初始化
+     * @return true            成功
+     * @return false           失败
+     */
+    static bool multiboot2_init(void);
+
+    /**
+     * @brief 迭代器
+     * @param  _fun            迭代操作
+     * @param  _data           数据
+     */
+    static void multiboot2_iter(bool (*_fun)(const iter_data_t *, void *),
+                                void *_data);
+
+    /**
+     * @brief 获取内存信息
+     * @param  _iter_data      迭代变量
+     * @param  _data           数据
+     * @return true            成功
+     * @return false           失败
+     */
+    static bool get_memory(const iter_data_t *_iter_data, void *_data);
+};
+
+namespace BOOT_INFO {
+    /// 魔数
+    extern "C" uint32_t multiboot2_magic;
+};
+
+#endif /* _MULTIBOOT2_H_ */

+ 262 - 13
kernel/head.S

@@ -4,11 +4,247 @@
 
 #include "common/asm.h"
 
-.section .text
 
-.global _start
+// 以下是来自 multiboot2 规范的定义
+//  How many bytes from the start of the file we search for the header.
+#define MULTIBOOT_SEARCH 32768
+#define MULTIBOOT_HEADER_ALIGN 8
+
+//  The magic field should contain this.
+#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
+
+//  This should be in %eax.
+#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
+
+//  Alignment of multiboot modules.
+#define MULTIBOOT_MOD_ALIGN 0x00001000
+
+//  Alignment of the multiboot info structure.
+#define MULTIBOOT_INFO_ALIGN 0x00000008
+
+//  Flags set in the 'flags' member of the multiboot header.
+
+#define MULTIBOOT_TAG_ALIGN 8
+#define MULTIBOOT_TAG_TYPE_END 0
+#define MULTIBOOT_TAG_TYPE_CMDLINE 1
+#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
+#define MULTIBOOT_TAG_TYPE_MODULE 3
+#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
+#define MULTIBOOT_TAG_TYPE_BOOTDEV 5
+#define MULTIBOOT_TAG_TYPE_MMAP 6
+#define MULTIBOOT_TAG_TYPE_VBE 7
+#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
+#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
+#define MULTIBOOT_TAG_TYPE_APM 10
+#define MULTIBOOT_TAG_TYPE_EFI32 11
+#define MULTIBOOT_TAG_TYPE_EFI64 12
+#define MULTIBOOT_TAG_TYPE_SMBIOS 13
+#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
+#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
+#define MULTIBOOT_TAG_TYPE_NETWORK 16
+#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
+#define MULTIBOOT_TAG_TYPE_EFI_BS 18
+#define MULTIBOOT_TAG_TYPE_EFI32_IH 19
+#define MULTIBOOT_TAG_TYPE_EFI64_IH 20
+#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
+
+#define MULTIBOOT_HEADER_TAG_END 0
+#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
+#define MULTIBOOT_HEADER_TAG_ADDRESS 2
+#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
+#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
+#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
+#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
+#define MULTIBOOT_HEADER_TAG_EFI_BS 7
+#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
+#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
+#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
+
+#define MULTIBOOT_ARCHITECTURE_I386 0
+#define MULTIBOOT_ARCHITECTURE_MIPS32 4
+#define MULTIBOOT_HEADER_TAG_OPTIONAL 1
+
+#define MULTIBOOT_LOAD_PREFERENCE_NONE 0
+#define MULTIBOOT_LOAD_PREFERENCE_LOW 1
+#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
+
+#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
+#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
+
+
+// 直接用 -m64 编译出来的是 64 位代码,
+// 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。
+// 得加一层跳转到 64 位的 -m32 代码,开启 long 模式后再跳转到以 -m64 编译的代码中
+// 对于 x86_64,需要在启动阶段进入长模式(IA32E),这意味着需要一个临时页表
+// See https://wiki.osdev.org/Creating_a_64-bit_kernel: 
+// With a 32-bit bootstrap in your kernel
+
+// 这部分是从保护模式启动 long 模式的代码
+// 工作在 32bit
+// 声明这一段代码以 32 位模式编译
+.code32
+
+// multiboot2 文件头
+// 计算头长度
+.SET HEADER_LENGTH, multiboot_header_end - multiboot_header
+// 计算校验和
+.SET CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + HEADER_LENGTH)
+// 8 字节对齐
+.align MULTIBOOT_HEADER_ALIGN
+// 声明所属段
+.section .multiboot_header
+multiboot_header:
+    // 魔数
+    .long MULTIBOOT2_HEADER_MAGIC
+    // 架构
+    .long MULTIBOOT_ARCHITECTURE_I386
+    // 头长度
+    .long HEADER_LENGTH
+    // 校验和
+    .long CHECKSUM
+    // 添加其它内容在此,详细信息见 Multiboot2 Specification version 2.0.pdf
+	.short MULTIBOOT_HEADER_TAG_END
+    // 结束标记
+    .short 0
+    .long 8
+multiboot_header_end:
+
+// 临时页表 4KB/页
+.section .data
+.align 0x1000
+pml4:
+    .skip 0x1000
+pdpt:
+    .skip 0x1000
+pd:
+    .skip 0x1000
+pt:
+    .skip 0x1000
+
+// 临时 GDT
+.align 16
+gdt64:
+null_desc:
+    .short 0xFFFF
+    .short 0
+    .byte 0
+    .byte 0
+    .byte 0
+    .byte 0
+code_desc:
+    .short 0
+    .short 0
+    .byte 0
+    .byte 0x9A
+    .byte 0x20
+    .byte 0
+data_desc:
+    .short 0
+    .short 0
+    .byte 0
+    .byte 0x92
+    .byte 0
+    .byte 0
+user_code_desc:
+    .short 0
+    .short 0
+    .byte 0
+    .byte 0xFA
+    .byte 0x20
+    .byte 0
+user_data_desc:
+    .short 0
+    .short 0
+    .byte 0
+    .byte 0xF2
+    .byte 0
+    .byte 0
+gdt64_pointer:
+    .short gdt64_pointer-gdt64-1
+    .quad gdt64
+gdt64_pointer64:
+    .short gdt64_pointer-gdt64-1
+    .quad gdt64
 
+.section .text
+.global _start
+.type _start, @function
+# 在 multiboot2.cpp 中定义
+.extern boot_info_addr
+.extern multiboot2_magic
 _start:
+    // 关中断
+    cli
+    // multiboot2_info 结构体指针
+    //mov %ebx, boot_info_addr
+    // 魔数
+    //mov %eax, multiboot2_magic
+    / 从保护模式跳转到长模式
+    // 1. 允许 PAE
+    mov %cr4, %eax
+    or $(1<<5), %eax
+    mov %eax, %cr4
+    // 2. 设置临时页表
+    // 最高级
+    mov $pml4, %eax
+    mov $pdpt, %ebx
+    or $0x3, %ebx
+    mov %ebx, 0(%eax)
+
+    // 次级
+    mov $pdpt, %eax
+    mov $pd, %ebx
+    or $0x3, %ebx
+    mov %ebx, 0(%eax)
+
+    // 次低级
+    mov $pd, %eax
+    mov $pt, %ebx
+    or $0x3, %ebx
+    mov %ebx, 0(%eax)
+
+    // 最低级
+    // 循环 512 次,填满一页
+    mov $512, %ecx
+    mov $pt, %eax
+    mov $0x3, %ebx
+.fill_pt:
+    mov %ebx, 0(%eax)
+    add $0x1000, %ebx
+    add $8, %eax
+    loop .fill_pt
+
+    // 填写 CR3
+    mov $pml4, %eax
+    mov %eax, %cr3
+
+    // 3. 切换到 long 模式
+    mov $0xC0000080, %ecx
+    rdmsr
+    or $(1<<8), %eax
+    wrmsr
+
+    // 4. 开启分页
+    mov %cr0, %eax
+    or $(1<<31), %eax
+    mov %eax, %cr0
+
+    // 5. 重新设置 GDT
+    mov $gdt64_pointer, %eax
+    lgdt 0(%eax)
+
+    // 6. 跳转到 64 位代码执行
+    jmp $0x8, $_start64
+    hlt
+    ret
+
+.section .text
+
+.code64
+.global _start64
+.type _start64, @function
+.extern Start_Kernel
+ENTRY(_start64)
     // 初始化寄存器
     mov $0x10, %ax
     mov %ax, %ds
@@ -28,14 +264,26 @@ _start:
     mov %ax, %fs
     mov %ax, %ss
     mov %ax, %gs
-
+    
     movq $0x7e00, %rsp
-
+    
+    // 2. 设置临时页表
+    // 最高级
+    mov $__PML4E, %eax
+    mov $__PDPTE, %ebx
+    or $0x7, %ebx
+    mov %ebx, 0(%eax)
+
+    // 次级
+    mov $__PDPTE, %eax
+    mov $__PDE, %ebx
+    or $0x7, %ebx
+    mov %ebx, 0(%eax)
 // ==== 加载CR3寄存器
-    movq $0x101000, %rax //设置页目录基地址
+    movq $__PML4E, %rax //设置页目录基地址
     movq %rax, %cr3
-    movq switch_seg(%rip), %rax
 
+    movq switch_seg(%rip), %rax
     // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器
     // 实在是太妙了!Amazing!
     pushq $0x08 //段选择子
@@ -44,9 +292,11 @@ _start:
 
 // 64位模式的代码
 switch_seg:
+
     .quad entry64
 
 entry64:
+
     movq $0x10, %rax
     movq %rax, %ds
     movq %rax, %es
@@ -119,11 +369,8 @@ SetUp_TSS64:
     // mov $0x50, %ax // 设置起始地址为80
     // ltr %ax
 
-    // 切换到内核主程序
-    movq go_to_kernel(%rip), %rax
-    pushq $0x08
-    pushq %rax
-    lretq
+
+    call Start_Kernel
 
 go_to_kernel:
     .quad Start_Kernel
@@ -148,13 +395,14 @@ ENTRY(_stack_start)
 
 
 // 初始化页表
-.align 8 //设置为8byte对齐
+
+.align 0x1000 //设置为8byte对齐
 .org 0x1000 //设置页表位置为内核执行头程序的0x1000处
 
 __PML4E:
     .quad 0x102007 // 用户访问,可读写,已存在, 地址在31~12位
     .fill	255,8,0
-	.quad	0x102007
+	.quad 0x102007
 	.fill	255,8,0
 
 .org	0x2000
@@ -185,6 +433,7 @@ __PDE:
 
 // GDT表
 .section .data
+.align 16
 .global GDT_Table // 使得GDT可以被外部程序引用或者访问
 
 GDT_Table:

+ 6 - 1
kernel/link.lds

@@ -5,10 +5,14 @@ ENTRY(_start)
 SECTIONS
 {
 
-	. = 0xffff800000000000 + 0x100000;
+	. = 0;
+
+
+	. = 1M;
 	.text :
 	{
 		_text = .;
+		*(.multiboot_header)
 		*(.text)
 
 		_etext = .;
@@ -19,6 +23,7 @@ SECTIONS
 		_data = .;
 		*(.data)
 		
+        *(.eh_frame)
 		_edata = .;
 	}
 	.rodata : 

+ 9 - 14
kernel/main.c

@@ -13,10 +13,10 @@
 #include "syscall/syscall.h"
 
 unsigned int *FR_address = (unsigned int *)0xffff800000a00000; //帧缓存区的地址
-// char fxsave_region[512] __attribute__((aligned(16)));
+                                                               // char fxsave_region[512] __attribute__((aligned(16)));
 
- struct memory_desc memory_management_struct = {{0}, 0};
-//struct Global_Memory_Descriptor memory_management_struct = {{0}, 0};
+struct memory_desc memory_management_struct = {{0}, 0};
+// struct Global_Memory_Descriptor memory_management_struct = {{0}, 0};
 
 void show_welcome()
 {
@@ -37,7 +37,6 @@ void show_welcome()
     printk_color(0x00e0ebeb, 0x00e0ebeb, "                                \n\n");
 }
 
-
 // 测试内存管理单元
 /*
 void test_mm()
@@ -63,18 +62,16 @@ void test_mm()
 // 初始化系统各模块
 void system_initialize()
 {
-// 初始化printk
-    init_printk(1440, 900, FR_address, 1440 * 900 * 4, 8, 16);
-
+    // 初始化printk
+    init_printk(1024, 768, FR_address, 1024 * 768 * 4, 8, 16);
+    printk("11111\n");
     load_TR(10); // 加载TR寄存器
-
+    while(1);
     // 初始化任务状态段表
     ul tss_item_addr = 0xffff800000007c00;
-    
+
     set_TSS64(_stack_start, _stack_start, _stack_start, tss_item_addr, tss_item_addr,
               tss_item_addr, tss_item_addr, tss_item_addr, tss_item_addr, tss_item_addr);
-              
-    
 
     // 初始化中断描述符表
     init_sys_vector();
@@ -96,12 +93,10 @@ void Start_Kernel(void)
 {
 
     system_initialize();
-    
+
     // show_welcome();
     // test_mm();
 
-   
-
     while (1)
         ;
 }

+ 2 - 0
kernel/process/process.c

@@ -264,6 +264,8 @@ unsigned long do_fork(struct pt_regs *regs, unsigned long clone_flags, unsigned
     thd->rbp = (ul)tsk + STACK_SIZE;
     thd->rip = regs->rip;
     thd->rsp = (ul)tsk + STACK_SIZE - sizeof(struct pt_regs);
+    thd->fs = KERNEL_DS;
+	thd->gs = KERNEL_DS;
 
     // 若进程不是内核层的进程,则跳转到ret from system call
     if (!(tsk->flags & PF_KTHREAD))

+ 100 - 0
run.sh

@@ -0,0 +1,100 @@
+# ======检查是否以sudo运行=================
+#uid=`id -u`
+#if [ ! $uid == "0" ];then
+#  echo "请以sudo权限运行"
+#  exit
+#fi
+
+# 第一个参数如果是--notbuild 那就不构建,直接运行
+if [ ! "$1" == "--nobuild" ]; then
+    echo "开始构建..."
+    make all -j 16
+    make clean
+fi
+
+IA32_USE_QEMU=1
+bochsrc="./bochsrc"
+ARCH="x86_64"
+
+# 内核映像
+kernel='./bin/kernel/kernel.elf'
+iso_boot_grub='./iso/boot/grub'
+iso_boot='./iso/boot/'
+iso='./DragonOS.iso'
+iso_folder='./iso/'
+
+
+# toolchain
+OS=`uname -s`
+if [ "${OS}" == "Linux" ]; then
+    GRUB_PATH="$(dirname $(which grub-file))"
+elif [ "${OS}" == "Darwin" ]; then
+    GRUB_PATH="$(pwd)/tools/grub-2.04/build/grub/bin"
+fi
+export PATH="${GRUB_PATH}:$PATH"
+
+# ==============检查文件是否齐全================
+
+
+bins[0]=${kernel}
+
+for file in ${bins[*]};do
+if [ ! -x $file ]; then
+  echo "$file 不存在!"
+  exit
+  fi
+done
+
+# ===============文件检查完毕===================
+
+# 如果是 i386/x86_64,需要判断是否符合 multiboot2 标准
+if [ ${ARCH} == "i386" ] || [ ${ARCH} == "x86_64" ]; then
+    if ${GRUB_PATH}/grub-file --is-x86-multiboot2 ${kernel}; then
+        echo Multiboot2 Confirmed!
+    else
+        echo NOT Multiboot2!
+        exit
+    fi
+fi
+# 检测路径是否合法,发生过 rm -rf -f /* 的惨剧
+if [ "${iso_boot}" == "" ]; then
+    echo iso_boot path error.
+else
+    mkdir -p ${iso_boot}
+    rm -rf -f ${iso_boot}/*
+fi
+
+# 设置 grub 相关数据
+if [ ${ARCH} == "i386" ] || [ ${ARCH} == "x86_64" ]; then
+    cp ${kernel} ${iso_boot}
+    mkdir ${iso_boot_grub}
+    touch ${iso_boot_grub}/grub.cfg
+    echo 'set timeout=15
+    set default=0
+    menuentry "DragonOS" {
+       multiboot2 /boot/kernel.elf "KERNEL_ELF"
+   }' >${iso_boot_grub}/grub.cfg
+fi
+
+${GRUB_PATH}/grub-mkrescue -o ${iso} ${iso_folder}
+rm -rf ${iso_folder}
+# 进行启动前检查
+flag_can_run=0
+
+if [ -d "${iso_folder}" ]; then
+  flag_can_run=0
+  echo "${iso_folder} 文件夹未删除!"
+else
+  flag_can_run=1
+fi
+
+if [ $flag_can_run -eq 1 ]; then
+  if [ ${IA32_USE_QEMU} == 0 ]; then
+        bochs -q -f ${bochsrc} -rc ./tools/bochsinit
+    else
+        qemu-system-x86_64 -cdrom ${iso} -m 128M \
+        -monitor telnet::2333,server,nowait -serial stdio
+    fi
+else
+  echo "不满足运行条件"
+fi

+ 0 - 80
run_in_bochs.sh

@@ -1,80 +0,0 @@
-# ======检查是否以sudo运行=================
-uid=`id -u`
-if [ ! $uid == "0" ];then
-  echo "请以sudo权限运行"
-  exit
-fi
-
-# 第一个参数如果是--notbuild 那就不构建,直接运行
-if [ ! "$1" == "--nobuild" ]; then
-    echo "开始构建..."
-    make all -j 16
-    make clean
-fi
-
-# ==============检查文件是否齐全================
-
-bins[0]=bin/bootloader/boot.bin
-bins[1]=bin/bootloader/loader.bin
-bins[2]=bin/boot.img
-bins[3]=bin/kernel/kernel.bin
-
-for file in ${bins[*]};do
-if [ ! -x $file ]; then
-  echo "$file 不存在!"
-  exit
-  fi
-done
-
-# ===============文件检查完毕===================
-
-
-# =========将引导程序写入boot.img=============
-dd if=bin/bootloader/boot.bin of=bin/boot.img bs=512 count=1 conv=notrunc
-
-# =========创建临时文件夹==================
-# 判断临时文件夹是否存在,若不存在则创建新的
-if [ ! -d "tmp/" ]; then
-  mkdir tmp/
-  echo "创建了tmp文件夹"
-fi
-
-# ==============挂载boot.img=============
-  mkdir tmp/boot
-  mount bin/boot.img tmp/boot -t vfat -o loop
-
-  # 检查是否挂载成功
-  if  mountpoint -q tmp/boot
-   then
-      echo "成功挂载 boot.img 到 tmp/boot"
-      # ========把loader.bin复制到boot.img==========
-      cp bin/bootloader/loader.bin tmp/boot
-      # ========把内核程序复制到boot.img======
-      cp bin/kernel/kernel.bin tmp/boot
-      sync
-      # 卸载磁盘
-      umount tmp/boot
-  else
-    echo "挂载 boot.img 失败!"
-  fi
-
-
-
-# 运行结束后删除tmp文件夹
-rm -rf tmp
-
-# 进行启动前检查
-flag_can_run=0
-
-if [ -d "tmp/" ]; then
-  flag_can_run=0
-  echo "tmp文件夹未删除!"
-else
-  flag_can_run=1
-fi
-
-if [ $flag_can_run -eq 1 ]; then
-  bochs -f ./bochsrc -q
-else
-  echo "不满足运行条件"
-fi

+ 0 - 78
run_in_qemu.sh

@@ -1,78 +0,0 @@
-# ======检查是否以sudo运行=================
-uid=`id -u`
-if [ ! $uid == "0" ];then
-  echo "请以sudo权限运行"
-  exit
-fi
-
-# 第一个参数如果是--notbuild 那就不构建,直接运行
-if [ ! "$1" == "--nobuild" ]; then
-    echo "开始构建..."
-    make all
-    make clean
-fi
-
-# ==============检查文件是否齐全================
-bins[0]=bin/bootloader/boot.bin
-bins[1]=bin/bootloader/loader.bin
-bins[2]=bin/boot.img
-bins[3]=bin/kernel/kernel.bin
-
-for file in ${bins[*]};do
-if [ ! -x $file ]; then
-  echo "$file 不存在!"
-  exit
-  fi
-done
-# ===============文件检查完毕===================
-
-
-# =========将引导程序写入boot.img=============
-dd if=bin/bootloader/boot.bin of=bin/boot.img bs=512 count=1 conv=notrunc
-
-# =========创建临时文件夹==================
-# 判断临时文件夹是否存在,若不存在则创建新的
-if [ ! -d "tmp/" ]; then
-  mkdir tmp/
-  echo "创建了tmp文件夹"
-fi
-
-# ==============挂载boot.img=============
-  mkdir tmp/boot
-  mount bin/boot.img tmp/boot -t vfat -o loop
-
-  # 检查是否挂载成功
-  if  mountpoint -q tmp/boot
-   then
-      echo "成功挂载 boot.img 到 tmp/boot"
-      # ========把loader.bin复制到boot.img==========
-      cp bin/bootloader/loader.bin tmp/boot
-      # ========把内核程序复制到boot.img======
-      cp bin/kernel/kernel.bin tmp/boot
-      sync
-      # 卸载磁盘
-      umount tmp/boot
-  else
-    echo "挂载 boot.img 失败!"
-  fi
-
-
-
-# 运行结束后删除tmp文件夹
-rm -rf tmp
-
-# 进行启动前检查
-flag_can_run=0
-
-if [ -d "tmp/" ]; then
-  flag_can_run=0
-  echo "tmp文件夹未删除!"
-else
-  flag_can_run=1
-fi
-
-if [ $flag_can_run -eq 1 ]; then
-  qemu-system-x86_64 -s -S -m 2048 -fda bin/boot.img  
-else
-  echo "不满足运行条件"
-fi