2014-11-20 36 views
2

编辑:plz跳到我的第二篇文章下面...从汇编bootloader调用C内核

我正在寻找一个极简主义的方式从我的引导程序进入我的内核。 你有任何可行的例子吗?

这里是进入保护模式的引导程序: “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"; 
} 

感谢您的帮助,

到目前为止,这是我试图这样做,但它不工作: 我只是p UT斯达康内核为0x8000与LD和猫串联,然后从引导程序跳转为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 
+0

'jmp哪里有你加载你的主'?我希望你可以在任何地方加载它,在你的代码中看不到) – Jester 2014-11-20 23:26:26

+1

你可能想把'main()'变成一个循环,否则在计算机执行内存之前你可能看不到任何东西*通过'main()'在几分之一秒内完成。 – EOF 2014-11-20 23:35:32

+0

是的,要么使用'call'并保留'jmp $'。 – Jester 2014-11-20 23:39:05

回答

1

这是我successed做的,但写在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.c

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

而下面的脚本替换kernel.asm:

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位 但它不起作用......请帮忙!

+0

通过在kernel.c中将“X”替换为'X'可以解决问题 – user3166684 2015-05-03 04:26:47

3

哦,这绝对有效。这似乎只是你对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即可!你去!

+0

您是对的!感谢您的帮助 – user3166684 2015-05-01 06:46:39

+0

你能解释我的代码有什么问题吗?我真的不明白。在此先感谢 – user3166684 2015-05-01 08:25:21

1

下面是用于输入64位模式中的可行的解决方案,并增加了一个C内核入口点:

引导。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 
+0

只有当此代码正常工作。 – 2016-10-30 10:59:01

1

加载内核是在使用[segment base:offset]寻址的实模式下完成的。要在保护模式下访问缓冲区地址(KERNEL_OFFSET)的相同位置,请将内存地址值向左移4次。因此,而不是0x1000,跳转到0x10000。我建议你先使用“jmp”,然后再将其改为“call”,并为(;;)添加无限大;在主函数结束时循环。

这里的目标是编译并链接32位内核,以便在32位保护模式下调用它。 因此,将机器选项设置为您的gcc命令的-m32并将“-m elf_i386”添加到ld。通过删除“--oformat binary”选项来使用默认格式的精灵。

+0

如果你从LD中删除'--oformat binary',你或者推断你应该使用启动加载程序,让你进入保护模式并加载ELF图像(即GRUB),或者你将不得不使用类似OBJCOPY的东西从ELF图像中获取平面二进制文件。我通常从链接中排除'--oformat binary',这样我就可以生成带有调试信息的ELF图像,然后使用OBJCOPY获取最终的平面二进制文件。这样我可以使用像QEMU这样的远程符号调试。 – 2016-02-01 05:41:28

+0

迈克尔! – 2016-02-04 03:43:38