簡體   English   中英

從程序集引導程序中調用C內核

[英]Call C kernel from assembly bootloader

編輯:請跳到我下面的第二篇文章...

我正在尋找一種極簡主義的方式從我的引導程序進入我的內核。 你有任何可行的例子嗎?

這是進入保護模式的引導加載程序:“boot.asm”

[org 0x7C00]

mov bp , 0x9000
mov sp , bp

cli
lgdt [gdt_descriptor] 

; Enter PM
mov eax, cr0
or  eax, 0x1
mov cr0, eax

jmp 0x8:init_pm 


[bits 32]
init_pm :
    mov ax, 0x10
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    ;Tried to call my C function from here 
    call 0x8000      ; Kernel entry point       

    jmp $


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 16]

GDT:
;null : 
    dd 0x0 
    dd 0x0

;code : 
    dw 0xffff       ;Limit
    dw 0x0          ;Base
    db 0x0          ;Base
    db 0b10011010   ;1st flag, Type flag
    db 0b11001111   ;2nd flag, Limit
    db 0x0          ;Base

;data : 
    dw 0xffff       
    dw 0x0          
    db 0x0
    db 0b10010010 
    db 0b11001111 
    db 0x0

gdt_descriptor :
    dw $ - GDT - 1      ;16-bit size
    dd GDT              ;32-bit start address


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Bootsector padding
times 510-($-$$) db 0
dw 0xaa55

這里是我想從引導程序中調用的內核入口點:“kernel.c”

void main() {
    char * vga = (char *) 0xb8000 ;
    *vga = "X";
}

謝謝你的幫助,

到目前為止,這是我試圖做的,但它不起作用:我只是用ld將內核置於0x8000並與cat連接,然后從引導加載程序跳轉到0x8000

nasm boot.asm -f bin -o boot.bin
gcc -ffreestanding -c kernel.c -o kernel.o
ld -o kernel.bin -Ttext 0x8000 kernel.o --oformat binary
cat boot.bin kernel.bin > os-image

哦,這絕對有效。 它似乎只是你對C的了解,真的。

我改變了你的C內核並得到了這個:

void main (void) 
{
   unsigned char* vga = (unsigned char*) 0xb8000;
   vga[0] = 'X'; //need to make sure that this is a character
   vga[1] = 0x09; //append the attribute byte
   for(;;); //make sure our kernel never stops, with an infinite loop
}

這似乎解決了我的問題! 現在一切正常,只需在該文件中寫一個shell然后BAM! 你去吧!

這是一個可行的解決方案,用於進入64位模式並添加一個C內核入口點:

boot.asm

[org 0x7c00]


KERNEL_ADDRESS equ 0x100000


cli

lgdt [gdt_descriptor] 

;Switch to PM
mov eax, cr0 
or eax, 0x1 
mov cr0, eax 

jmp 0x8:init_pm 


[bits 32]

init_pm :
    mov ax, 0x10
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax


call build_page_tables


;Enable PAE
mov eax, cr4                 
or eax, 1 << 5               
mov cr4, eax

;# Optional : Enable global-page mechanism by setting CR0.PGE bit to 1
mov eax, cr4                 
or eax, 1 << 7               
mov cr4, eax

;Load CR3 with PML4 base address
;NB: in some examples online, the address is not offseted as it seems to
;be in the proc datasheet (if you were wondering about this strange thing).
mov eax, 0x1000
mov cr3, eax

;Set LME bit in EFER register (address 0xC0000080)
mov ecx, 0xC0000080     ;operand of 'rdmsr' and 'wrmsr'
rdmsr                   ;read before pr ne pas écraser le contenu
or eax, 1 << 8          ;eax : operand de wrmsr
wrmsr

;Enable paging by setting CR0.PG bit to 1
mov eax, cr0
or eax, (1 << 31)
mov cr0, eax

;Load 64-bit GDT
lgdt [gdt64_descriptor]

;Jump to code segment in 64-bit GDT
jmp 0x8:init_lm


[bits 64]

init_lm:
    mov ax, 0x10
    mov fs, ax          ;other segments are ignored
    mov gs, ax

    mov rbp, 0x90000    ;set up stack
    mov rsp, rbp

    ;Load kernel from disk
    xor ebx, ebx        ;upper 2 bytes above bh in ebx is for cylinder = 0x0
    mov bl, 0x2         ;read from 2nd sectors
    mov bh, 0x0         ;head
    mov ch, 1           ;read 1 sector
    mov rdi, KERNEL_ADDRESS
    call ata_chs_read


    jmp KERNEL_ADDRESS

    jmp $



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[bits 16]
;; http://wiki.osdev.org/ATA_in_x86_RealMode_%28BIOS%29


;load_loader:
    ;;!! il faut rester sur le meme segment, ie <0x10000 (=2**16)
    ;mov bx, LOADER_OFFSET  
    ;mov dh, 1          ;load 1 sector (max allowed by BIOS is 128)
    ;mov dl, 0x80       ;drive number

    ;mov ah, 0x02       ;read function
    ;mov al, dh
    ;mov ch, 0x00       ;cylinder
    ;mov dh, 0x00       ;head
    ;; !! Sector is 1-based, and not 0-based
    ;mov cl, 0x02       ;1st sector to read 
    ;int 0x13
    ;ret


[bits 32]
build_page_tables:
    ;PML4 starts at 0x1000
    ;il faut laisser la place pour tte la page PML4/PDP/PD ie. 0x1000

    ;PML4 @ 0x1000
    mov eax, 0x2000         ;PDP base address            
    or eax, 0b11            ;P and R/W bits
    mov ebx, 0x1000         ;MPL4 base address
    mov [ebx], eax

    ;PDP @ 0x2000; maps 64Go
    mov eax, 0x3000         ;PD base address
    mov ebx, 0x2000         ;PDP physical address   
    mov ecx, 64             ;64 PDP

    build_PDP:
        or eax, 0b11    
        mov [ebx], eax
        add ebx, 0x8
        add eax, 0x1000     ;next PD page base address
        loop build_PDP

    ;PD @ 0x3000 (ends at 0x4000, fits below 0x7c00)
    ; 1 entry maps a 2MB page, the 1st starts at 0x0
    mov eax, 0x0            ;1st page physical base address     
    mov ebx, 0x3000         ;PD physical base address
    mov ecx, 512                        

    build_PD:
        or eax, 0b10000011      ;P + R/W + PS (bit for 2MB page)
        mov [ebx], eax
        add ebx, 0x8
        add eax, 0x200000       ;next 2MB physical page
        loop build_PD

    ;(tables end at 0x4000 => fits before Bios boot sector at 0x7c00)
    ret



;=============================================================================
; ATA read sectors (CHS mode) 
; Max head index is 15, giving 16 possible heads
; Max cylinder index can be a very large number (up to 65535)
; Sector is usually always 1-63, sector 0 reserved, max 255 sectors/track
; If using 63 sectors/track, max disk size = 31.5GB
; If using 255 sectors/track, max disk size = 127.5GB
; See OSDev forum links in bottom of [http://wiki.osdev.org/ATA]
;
; @param EBX The CHS values; 2 bytes, 1 byte (BH), 1 byte (BL) accordingly
; @param CH The number of sectors to read
; @param RDI The address of buffer to put data obtained from disk               
;
; @return None
;=============================================================================
[bits 64]
ata_chs_read:   pushfq
                push rax
                push rbx
                push rcx
                push rdx
                push rdi

                mov rdx,1f6h            ;port to send drive & head numbers
                mov al,bh               ;head index in BH
                and al,00001111b        ;head is only 4 bits long
                or  al,10100000b        ;default 1010b in high nibble
                out dx,al

                mov rdx,1f2h            ;Sector count port
                mov al,ch               ;Read CH sectors
                out dx,al

                mov rdx,1f3h            ;Sector number port
                mov al,bl               ;BL is sector index
                out dx,al

                mov rdx,1f4h            ;Cylinder low port
                mov eax,ebx             ;byte 2 in ebx, just above BH
                mov cl,16
                shr eax,cl              ;shift down to AL
                out dx,al

                mov rdx,1f5h            ;Cylinder high port
                mov eax,ebx             ;byte 3 in ebx, just above byte 2
                mov cl,24
                shr eax,cl              ;shift down to AL
                out dx,al

                mov rdx,1f7h            ;Command port
                mov al,20h              ;Read with retry.
                out dx,al

.still_going:   in al,dx
                test al,8               ;the sector buffer requires servicing.
                jz .still_going         ;until the sector buffer is ready.

                mov rax,512/2           ;to read 256 words = 1 sector
                xor bx,bx
                mov bl,ch               ;read CH sectors
                mul bx
                mov rcx,rax             ;RCX is counter for INSW
                mov rdx,1f0h            ;Data port, in and out
                rep insw                ;in to [RDI]

                pop rdi
                pop rdx
                pop rcx
                pop rbx
                pop rax
                popfq
                ret


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 16]

GDT:
;null : 
    dd 0x0 
    dd 0x0

;code : 
    dw 0xffff       ;Limit
    dw 0x0          ;Base
    db 0x0          ;Base
    db 10011010b    ;1st flag, Type flag
    db 11001111b    ;2nd flag, Limit
    db 0x0          ;Base

;data : 
    dw 0xffff       
    dw 0x0          
    db 0x0
    db 10010010b 
    db 11001111b 
    db 0x0

gdt_descriptor :
    dw $ - GDT - 1      ;16-bit size
    dd GDT              ;32-bit start address



[bits 32]
;see manual 2, §4.8: most fields are ignored in long mode
GDT64:
;null;
    dq 0x0

;code
    dd 0x0
    db 0x0
    db 0b10011000   
    db 0b00100000
    db 0x0

;data
    dd 0x0
    db 0x0
    db 0b10010000   
    db 0b00000000
    db 0x0

gdt64_descriptor :
    dw $ - GDT64 - 1        ;16-bit size
    dd GDT64                ;32-bit start address


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 16]
times 510 -($-$$) db 0
dw 0xaa55

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

loader.asm:

 [bits 64]


;mov al, 'K'
;mov ah, 3 ; cyan
;mov edx, 0xb8000
;mov [edx], ax
;jmp $

extern main
global _start

_start:
  call main     ; Call our kernel's main() function
  hlt        

main.c

int main(void)
{
    //Prints a "X" on the upper-left corner
    char * vga = (char *) 0xb8000 ;
    *vga = 'X';

    while(1){};
    return 1;

}

build.sh:此腳本僅適用於64位主機

#!/bin/bash

nasm -f bin boot.asm -o boot.bin
nasm -f elf64 loader.asm -o loader.o

#cc -m64  -ffreestanding -fno-builtin -nostdlib -c main.c
cc -m64 -masm=intel -c main.c
ld  -Ttext 0x100000 -o kernel.elf loader.o main.o 
objcopy -R .note -R .comment -S -O binary kernel.elf kernel.bin

dd if=/dev/zero of=image.bin bs=512 count=2880
dd if=boot.bin of=image.bin conv=notrunc
dd if=kernel.bin of=image.bin conv=notrunc bs=512 seek=1

rm ./boot.bin ./kernel.bin ./main.o ./loader.o ./kernel.elf

qemu-system-x86_64  image.bin

這是我成功完成的事情,但是使用asm編寫的內核:

boot.asm:

[org 0x7c00]

KERNEL_OFFSET equ 0x1000

call load_kernel

;Switch PM
cli
lgdt [gdt_descriptor]

mov eax, cr0
or eax, 0x1
mov cr0, eax

jmp 0x8:init_pm

[bits 32]
init_pm :
   mov ax, 0x10
   mov ds, ax
   mov ss, ax
   mov es, ax
   mov fs, ax
   mov gs, ax

   mov ebp, 0x90000
   mov esp, ebp

   call KERNEL_OFFSET
   jmp $

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 16]

load_kernel:
   mov bx, KERNEL_OFFSET
   mov dh, 15
   mov dl, 0

   mov ah, 0x02
   mov al, dh
   mov ch, 0x00
   mov dh, 0x00
   mov cl, 0x02
   int 0x13
   ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 16]

GDT:
;null :
   dd 0x0
   dd 0x0

;code :
   dw 0xffff      ;Limit
   dw 0x0         ;Base
   db 0x0         ;Base
   db 10011010b    ;1st flag, Type flag
   db 11001111b    ;2nd flag, Limit
   db 0x0         ;Base

;data :
   dw 0xffff      
   dw 0x0         
   db 0x0
   db 10010010b
   db 11001111b
   db 0x0

gdt_descriptor :
   dw $ - GDT - 1       ;16-bit size
   dd GDT            ;32-bit start address

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times 510 -($-$$) db 0
dw 0xaa55

kernel.asm:

[bits 32]

mov al, 'K'
mov ah, 3 ; cyan
mov edx, 0xb8000
mov [edx], ax
jmp $

我構建了軟盤映像,以便:

nasm boot.asm -o boot.bin
dd if=/dev/zero of=image.bin bs=512 count=2880
dd if=boot.bin of=image.bin conv=notrunc

nasm kernel.asm -o kernel.bin
dd if=kernel.bin of=image.bin conv=notrunc bs=512 seek=1

qemu -fda image.bin -boot a

這種方式有效! 這幾乎就是......但是現在,我用這個kernel.c替換了kernel.asm

kernel.c

void main () {
   char * vga = (char *) 0xb8000 ;
   *vga = "X";
}

以下腳本:

nasm boot.asm -o boot.bin
dd if=/dev/zero of=image.bin bs=512 count=2880
dd if=boot.bin of=image.bin conv=notrunc

gcc -ffreestanding -m32 -c kernel32.c -o kernel.bin
dd if=kernel.bin of=image.bin conv=notrunc bs=512 seek=1

qemu -fda image.bin -boot a

請注意,我正在運行64位Linux發行版,因此我使用'-m32'gcc選項編譯為32位但是它不起作用......請幫助!

內核的加載是在使用[segment base:offset]尋址的實模式下完成的。 要在保護模式下訪問緩沖區地址(KERNEL_OFFSET)的相同位置,請將內存地址值向左移動4次。 因此,而不是0x1000,跳轉到0x10000。 我建議你先嘗試使用“jmp”,然后再將其改為“call”,並為(;;)添加無限; 循環在main函數的末尾。

這里的目標是以32位編譯和鏈接你的內核,以便它可以在32位保護模式下調用。 因此,將機器選項設置為gcc命令的-m32,並將“-m elf_i386”添加到ld。 通過刪除“--oformat binary”選項,也可以使用默認格式elf。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM