ospre 0.1.6

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


%include "asm/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