ospre 0.1.6

这是一个用于开发64位操作系统的前置工具,用于做boot、loader等工作。它需要你安装nasm编译器才能使用,使用方式请看文档
Documentation


pub fn loader()->&'static str{
    "
    ;
    ; 功能:清除屏幕,检测处理器是否支持64位模式;若支持,加载内核,设置临时4级页表,并开启IA32E模式
    ; Author:fengchen Xie
    ; Time:2022-04-20
    ;
    
    %include \"ospre/common/boot_loader_equ.asm\"
    [bits 32]
    org 0x7e00
    section loader align=4
        ;清除屏幕
        call clear_screen
    
        ;获取处理器支持的最大功能号,判断处理器是否支持IA32E模式
        mov eax,0x80000000
        cpuid
        cmp eax,0x80000000
        jle not_support_ia32e   ;不支持则打印信息
    
        mov eax,0x80000001
        cpuid
        bt edx,29
        jnc not_support_ia32e    ;不支持则打印信息
    
        ;读取loader64位程序部分,加载到0x100000处
        mov ecx,LOADER2_ALL_SECTORS
        mov eax,LOADER2_START_SECTOR
        mov ebx,LOADER2_CACHE_ADDRESS
    read_loader2:
        call read_one_sector
        add ebx,512
        inc eax
        loop read_loader2
    
        ;设置临时4级头表 、临时页目录指针表、临时页目录表
        mov ebx,FOUR_PAGE_ADDRESS
        mov ecx,1024*3
        mov edi,0x00
    .clear_tmp_page_space:                ;清理4级头表、临时页目录指针表、临时页目录表所需空间
        mov dword [ebx+edi*4],0x00
        inc edi
        loop .clear_tmp_page_space
    
    
        mov eax,THRER_PAGE_ADDRESS_FIRST | 3
        mov [FOUR_PAGE_ADDRESS+0x00],eax
        mov dword [FOUR_PAGE_ADDRESS+0x04],0x00      ;将4级头表第一项指向临时页目录指针表
    
        mov eax,TWO_PAGE_ADDRESS_FIRST | 3
        mov [THRER_PAGE_ADDRESS_FIRST+0x00],eax
        mov dword [THRER_PAGE_ADDRESS_FIRST+0x04],0x00 ;将临时页目录指针表的第一项指向临时页目录表
    
        xor eax,eax
        or eax,0x83
        mov ebx,TWO_PAGE_ADDRESS_FIRST
        mov ecx,5
    shoot_ten_m:
        mov dword [ebx+0x00],eax 
        mov dword [ebx+0x04],0x00          ;映射低端10M内存
        add eax,0x200000
        add ebx,8
        loop shoot_ten_m
    
        ;将4级头表物理地址写入cr3
        mov eax,FOUR_PAGE_ADDRESS
        mov cr3,eax
    
        ;开启PAE物理地址扩展功能
        mov eax,cr4
        bts eax,5
        mov cr4,eax
    
        ;设置型号专属寄存器IA32_EFER的IA32E模式允许位(第8位)
        mov ecx,0xc0000080 ;IA32_EFER寄存器编号
        rdmsr
        bts eax,8
        wrmsr
    
        ;开启分页功能,进入IA32E模式的兼容模式
        mov eax,cr0
        bts eax,31
        mov cr0,eax
        push dword 0x18
        push dword LOADER2_CACHE_ADDRESS
        retf     ;使用retf进行远跳转,跳转到loader64位代码
    
    
    ;-----------------------------当检测到处理器不支持ia32e模式时,提示not support ia32e ----------------
    not_support_ia32e:
        mov ebx,0xb8000
        mov esi,0x00
        mov edi,0x00
        mov eax,0x0700
        
    .print_error:
        mov al,[not_support_msg+edi]
        cmp al,0x00
        je .stop_print
        mov word [ebx+esi*2],ax
        inc edi
        inc esi
        loop .print_error
    
    .stop_print:
        cli
        hlt
        jmp $
    
    ;-----------------------------------------------清除屏幕例程---------------------------------------
    clear_screen:
        push eax
        push ebx
        push ecx
        push edx
    
        xor esi,esi
        mov ebx,0xb8000
        mov ecx,80*25
    .clear:
        mov word [ebx+esi*2],0x00
        inc esi
        loop .clear
    
        pop  edx
        pop  ecx
        pop  ebx
        pop  eax
    
        ret
    
    %include \"ospre/common/LBA28.asm\"
    
    ;----------------------------------------------下面是存放数据的区域------------------------------
    section data
    
    ;不支持ia32e模式提示消息
    not_support_msg:
        db \"cpu does not support  IA-32E\"

    "
}












pub fn loader2()->&'static str{
    "

    %include \"ospre/common/boot_loader_equ.asm\"

    TEMP_HEADER_ADDRESS equ 0x400000

    [bits 64]

    org 0x100000
    section loader2
        ;读取程序头部扇区,计算程序头表和elf64所占扇区数
        xor rax,rbx
        mov rbx,TEMP_HEADER_ADDRESS
        mov eax,KERNEL_START_SECTOR
        call read_one_sector

        xor rcx,rcx
        mov cx,[TEMP_HEADER_ADDRESS+56]           ;程序头表项数量

        xor rax,rax
        mov ax,[TEMP_HEADER_ADDRESS+54]           ;程序头表项大小

        xor rdx,rdx
        mul cx
        shl edx,16
        mov dx,ax                                 
        add edx,64                                ;当前edx中为程序头表+elf头大小
        add edx,512                               ;填补误差
        mov rax,rdx
        shr rax,9
        xor rcx,rcx
        mov ecx,eax                               ;当前ecx中为程序头表+elf头所占扇区


        ;读取程序头部扇区和程序头表,直接读取200个扇区,统计程序大小
        xor rbx,rbx
        mov rbx,TEMP_HEADER_ADDRESS
        mov eax,KERNEL_START_SECTOR
        ; mov rcx,200
    read_header:
        call read_one_sector
        inc eax
        add rbx,512
        loop read_header

        ;分析程序头表,统计程序大小
        mov rbx,[TEMP_HEADER_ADDRESS+32] 
        mov rax,TEMP_HEADER_ADDRESS
        add rbx,rax                               ;程序头表起始地址

        xor rcx,rcx
        mov cx,[TEMP_HEADER_ADDRESS+56]           ;程序头表项数量

        xor rax,rax
        mov ax,[TEMP_HEADER_ADDRESS+54]           ;程序头表项大小

        xor rdi,rdi                               ;用于保存程序大小

    calc_kernel_size:
        mov rsi,[rbx+32]
        add rdi,rsi
        add rbx,rax
        loop calc_kernel_size

        xor rdx,rdx
        mov cx,[TEMP_HEADER_ADDRESS+56]           ;程序头表项数量
        mul cx
        shl edx,16
        mov dx,ax

        add rdi,rdx
        add rdi,64                                ;当前rdi=程序头+ELF头+程序段总大小

        xor rax,rax
        xor rcx,rcx
        xor rdx,rdx
        mov ax,[TEMP_HEADER_ADDRESS+58]
        mov cx,[TEMP_HEADER_ADDRESS+60]

        mul cx
        shl edx,16
        mov dx,ax

        add rdi,rdx                               ;当前rdi=程序头+ELF头+程序段总大小+节头表

    ;根据程序大小分配地址空间
        add rdi,0x200000                          ;多分配2M左右的空间
        mov rax,rdi
        xor rdx,rdx
        mov rbx,0x200000
        div rbx                                   ;此时rax中就是内核映像需要分配的页数(按2M分页)

        mov r8,rax
        shl r8,21
        add r8,KERNEL_CACHE_ADDRESS               ;使用r8记录内核重定位时的相对地址

        mov rcx,rax
        shl rcx,1                                 ;由于需要存放内核映像以及加载内核,因此需要分配两倍内存
        lea rbx,[TWO_PAGE_ADDRESS_FIRST+5*8]
        lea rax,[0x200000*5]
        or rax,0x83

    alloc_mem:
        mov [rbx],rax
        add rbx,8
        add rax,0x200000
        loop alloc_mem

    ;正式读取内核
        mov rbx,KERNEL_CACHE_ADDRESS
        xor rax,rax
        mov eax,KERNEL_START_SECTOR
        mov rcx,KERNEL_ALL_SECTORS
    read_kernel:
        call read_one_sector
        add rbx,512
        inc eax
        loop read_kernel




    ;重定位内核程序,相对地址在r8中

        mov rbx,[KERNEL_CACHE_ADDRESS+32] 
        mov rax,KERNEL_CACHE_ADDRESS
        add rbx,rax                               ;程序头表起始地址

        xor rcx,rcx
        mov cx,[KERNEL_CACHE_ADDRESS+56]           ;程序头表项数量

        xor rax,rax
        mov ax,[KERNEL_CACHE_ADDRESS+54]           ;程序头表项大小


    relloc_kernel:
        mov rdi,[rbx+16]
        add rdi,r8                                 ;各个段被加载的目的地址

        mov rsi,KERNEL_CACHE_ADDRESS
        add rsi,[rbx+8]                            ;各个段的源地址

        mov r10,rcx

        mov rcx,[rbx+32]                           ;当前段所占字节数
        call mem_copy
        
        mov rcx,r10

        add rbx,rax

        loop relloc_kernel

        mov rax,[KERNEL_CACHE_ADDRESS+24]          
        add rax,r8                                 ;内核入口

        jmp rax



    ;内存拷贝过程
    mem_copy:
        push rsi
        push rdi
        push rcx

        cld
        rep movsb

        pop rcx
        pop rdi
        pop rsi

        ret


    ;参数说明:eax为起始扇区号,rbx为缓冲区地址
    read_one_sector:
        push rax
        push rbx
        push rcx
        push rdx
        push rsi

        mov rsi,rax

        mov al,1
        mov dx,0x1f2   ;向端口0x1f2写入读取的扇区数
        out dx,al

        mov rax,rsi

        inc dx
        out dx,al      ;向端口0x1f3写入起始扇区号0-7位

        inc dx
        shr eax,8
        out dx,al      ;向端口0x1f4写入起始扇区号8-15位

        inc dx
        shr eax,8
        out dx,al      ;向端口0x1f5写入起始扇区号16-23位

        inc dx
        shr eax,8
        or al,0xe0
        out dx,al      ;向端口0x1f6写入起始扇区号24-31位

        inc dx
        mov al,0x20
        out dx,al      ;向端口0x1f7写入磁盘读取命令0x20

    .waits:
        in al,dx
        and al,0x88
        cmp al,0x08
        jne .waits

        xor rcx,rcx
        mov cx,256
        mov dx,0x1f0

    .read_word:
        in ax,dx       ;循环读取512字节
        mov [rbx],ax
        add rbx,2
        loop .read_word

        pop  rsi
        pop  rdx
        pop  rcx
        pop  rbx
        pop  rax

        ret
    

    "
}