diff --git a/.gitignore b/.gitignore index 27aa8727..14ce310d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /cmake-build-debug/ /bin/ +./DragonOS.iso \ No newline at end of file diff --git a/Makefile b/Makefile index e5035b39..cdaa5dd5 100644 --- a/Makefile +++ b/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;\ diff --git a/bochsrc b/bochsrc index b2ac6e94..fd300488 100644 --- a/bochsrc +++ b/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 diff --git a/boot(empty).img b/boot(empty).img deleted file mode 100644 index 3525e064..00000000 Binary files a/boot(empty).img and /dev/null differ diff --git a/bootloader/Makefile b/bootloader/Makefile deleted file mode 100644 index e58c32f0..00000000 --- a/bootloader/Makefile +++ /dev/null @@ -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~ diff --git a/bootloader/boot.asm b/bootloader/boot.asm deleted file mode 100644 index f0f9722e..00000000 --- a/bootloader/boot.asm +++ /dev/null @@ -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为结尾 - diff --git a/bootloader/fat12.inc b/bootloader/fat12.inc deleted file mode 100644 index 104a757e..00000000 --- a/bootloader/fat12.inc +++ /dev/null @@ -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 ' \ No newline at end of file diff --git a/bootloader/loader.asm b/bootloader/loader.asm deleted file mode 100644 index 3d96e89e..00000000 --- a/bootloader/loader.asm +++ /dev/null @@ -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 \ No newline at end of file diff --git a/kernel/Makefile b/kernel/Makefile index 61f9001e..f0cfda68 100644 --- a/kernel/Makefile +++ b/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) \ No newline at end of file diff --git a/kernel/common/boot_info.h b/kernel/common/boot_info.h new file mode 100644 index 00000000..8e854fab --- /dev/null +++ b/kernel/common/boot_info.h @@ -0,0 +1,65 @@ + +/** + * @file boot_info.h + * @brief 启动信息接口 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#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_ */ \ No newline at end of file diff --git a/kernel/driver/multiboot2/boot.S b/kernel/driver/multiboot2/boot.S new file mode 100644 index 00000000..7941984d --- /dev/null +++ b/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 diff --git a/kernel/driver/multiboot2/multiboot2.c b/kernel/driver/multiboot2/multiboot2.c new file mode 100644 index 00000000..2e1bb0f5 --- /dev/null +++ b/kernel/driver/multiboot2/multiboot2.c @@ -0,0 +1,3 @@ + +int *boot_info_addr; +int *multiboot2_magic; \ No newline at end of file diff --git a/kernel/driver/multiboot2/multiboot2.cpp b/kernel/driver/multiboot2/multiboot2.cpp new file mode 100644 index 00000000..b92dcfed --- /dev/null +++ b/kernel/driver/multiboot2/multiboot2.cpp @@ -0,0 +1,104 @@ +/** + * @file multiboot2.cpp + * @brief multiboot2 解析实现 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#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(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; + } +}; \ No newline at end of file diff --git a/kernel/driver/multiboot2/multiboot2.h b/kernel/driver/multiboot2/multiboot2.h new file mode 100644 index 00000000..02386184 --- /dev/null +++ b/kernel/driver/multiboot2/multiboot2.h @@ -0,0 +1,334 @@ +/** + * @file multiboot2.h + * @brief multiboot2 解析 + * @author Zone.N (Zone.Niuzh@hotmail.com) + * @version 1.0 + * @date 2021-09-18 + * @copyright MIT LICENSE + * https://github.com/Simple-XX/SimpleKernel + * @par change log: + * + *
DateAuthorDescription + *
2021-09-18digmouse233迁移到 doxygen + *
+ */ + +#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_ */ \ No newline at end of file diff --git a/kernel/head.S b/kernel/head.S index f6d2f84e..b1f4af66 100644 --- a/kernel/head.S +++ b/kernel/head.S @@ -4,11 +4,247 @@ #include "common/asm.h" + +// 以下是来自 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 -.global _start - -_start: +.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: diff --git a/kernel/link.lds b/kernel/link.lds index 8961dbe9..b4897f60 100644 --- a/kernel/link.lds +++ b/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 : diff --git a/kernel/main.c b/kernel/main.c index c37643a6..102ca4b9 100644 --- a/kernel/main.c +++ b/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) ; } diff --git a/kernel/process/process.c b/kernel/process/process.c index 265cb268..f8bb6670 100644 --- a/kernel/process/process.c +++ b/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)) diff --git a/run.sh b/run.sh new file mode 100644 index 00000000..2ffd8605 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/run_in_bochs.sh b/run_in_bochs.sh deleted file mode 100644 index 16342f49..00000000 --- a/run_in_bochs.sh +++ /dev/null @@ -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 \ No newline at end of file diff --git a/run_in_qemu.sh b/run_in_qemu.sh deleted file mode 100644 index 6b2f1ef7..00000000 --- a/run_in_qemu.sh +++ /dev/null @@ -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 \ No newline at end of file