2017-11-03 14 views
2

私はアセンブリを学んでいる間にカーネルの仕組みを学び、ブート可能なx86_64カーネルをうまく作成する方法を学ぶために、私は問題がありました:
I 0xB8000のVGAバッファを使用して、 "main.c"(以下のすべてのファイル)にある関数を含むテキストを正常に出力しようとしました。これは、カーネルプロトタイプの32ビットバージョンと同じ方法ですが、開始ファイルが異なります。
ここで問題となるのは、32ビットバージョンと全く同じ機能を使用すると、画面に正常に印刷されますが、新しいファイルを使用してロングモード(multiboot.Sstart.S)になると、画面はqemuでテストすると黒くなり、数秒後にエラーメッセージでクラッシュします:私のプロトタイプのカーネル(x86_64)の問題

warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5] 
qemu-system-x86_64: Trying to execute code outside RAM or ROM at 0x00000000000a0000 

これはどうしてですか? VGAバッファはちょうど0xB8000にありません。* .Sファイルに何か問題がありますか?前もって感謝します!
ここにカーネルファイルを貼り付けます:
カーネルは、 "main.c"、 "start.S"、 "multiboot.S"、およびリンカースクリプト "linker.ld"の4つのファイルで構成されています。
この3つのファイルがエラーなしでリンクされ、コンパイルされ、ファイルは次のとおりです。これは、main.cのである(このファイルは単なるVGAカラーコードを定義、「basiccolors.h」を参照されます)

#include "basiccolors.h" 
#include <stddef.h> 
#include <stdint.h> 

volatile uint16_t* vga_buffer = (uint16_t*)0xB8000; /* memory location of the VGA textmode buffer */ 
/* Columns and rows of the VGA buffer */ 
const int VGA_COLS = 80; 
const int VGA_ROWS = 25; 

/* We start displaying text in the top-left of the screen (column = 0, row = 0) */ 
int term_col = 0; 
int term_row = 0; 
uint8_t term_color = WHITE_TXT; /* This color and others are defined in basiccolors.h */ 

/* term_init() : This function initiates the terminal by clearing it */ 
void term_init() 
{ 
    /* Clear the textmode buffer */ 
    for (int col = 0; col < VGA_COLS; col ++) 
    { 
     for (int row = 0; row < VGA_ROWS; row ++) 
     { 
      /* The VGA textmode buffer has size (VGA_COLS * VGA_ROWS) */ 
      /* Given this, we find an index into the buffer for our character */ 
      const size_t index = (VGA_COLS * row) + col; 
      /* Entries in the VGA buffer take the binary form BBBBFFFFCCCCCCCC, where: */ 
      /* - B is the background color */ 
      /* - F is the foreground color */ 
      /* - C is the ASCII character */ 
      /* Now we set the character to blank (a space character) */   
     vga_buffer[index] = ((uint16_t)term_color << 8) | ' '; 
     } 
    } 
} 

/* term_putc(char c) : This function places a single character onto the screen */ 
void term_putc(char c) 
{ 
    /* We don't want to display all characters, for example, the newline ones */ 
    switch (c) 
    { 
    case '\n': /* Newline characters should return the column to 0, and increment the row */ 
     { 
      term_col = 0; 
      term_row ++; 
      break; 
     } 

    default: /* Normal characters just get displayed and then increment the column */ 
     { 
     /* Like before, calculate the buffer index */   
     const size_t index = (VGA_COLS * term_row) + term_col; 
     vga_buffer[index] = ((uint16_t)term_color << 8) | c; 
     term_col ++; 
     break; 
     } 
    } 

/* We need to reset the column to 0, and increment the row to get to a new line */ 
    if (term_col >= VGA_COLS) 
    { 
     term_col = 0; 
     term_row ++; 
    } 

/* we get past the last row, so we need to reset both column and row to 0 in order to loop back to the top of the screen */ 
    if (term_row >= VGA_ROWS) 
    { 
     term_col = 0; 
     term_row = 0; 
    } 
    } 

/* term_print : prints an entire string onto the screen, remember to use the "\n" and that short of things */ 
void term_print(const char* str) 
{ 
    for (size_t i = 0; str[i] != '\0'; i ++) /* Keep placing characters until we hit the null-terminating character ('\0') */ 
     term_putc(str[i]); 
} 
/* Main function of the kernel, the one that is called at the end of the loading */ 
void kmain(void) 
{ 
    /* Now we should initialize the interfaces */ 
    term_init(); /* VGA basic interface, in "basicoutput.c/h" */ 
    term_print("CKA Cobalt release              [0-0-1]\n"); 
}; 

これはstart.Sです:

.extern kmain 

    .section .data 

    .align 16 
gdtr: 
gdtr_limit: 
    .word (global_descriptor_table_end - global_descriptor_table) - 1 
gdtr_pointer: 
    .int global_descriptor_table 

    .global global_descriptor_table 
global_descriptor_table: 
null_descriptor: 
    .quad 0x0000000000000000 
code_descriptor: 
    .quad 0x0020980000000000 
data_descriptor: 
    .quad 0x0000900000000000 
global_descriptor_table_end: 

    .global null_segment 
    .set null_segment, (null_descriptor - global_descriptor_table) 
    .global code_segment 
    .set code_segment, (code_descriptor - global_descriptor_table) 
    .global data_segment 
    .set data_segment, (data_descriptor - global_descriptor_table) 

multiboot_magic: 
    .space 4 
multiboot_info: 
    .space 4 

    .section .bss 

    .global kernel_pagetable 
    .align 0x1000 
kernel_pagetable: 
pml4: 
    .space 0x1000 
pdpt: 
    .space 0x1000 
pd: 
    .space 0x1000 
kernel_pagetable_end: 

    .global kernel_stack 
kernel_stack: 
    .space 0x1000 
kernel_stack_end: 

    .section .text 
    .code32 

    .global start 

start: 
    cli 

# store multiboot parameters in .data 
    mov %eax, multiboot_magic 
    mov %ebx, multiboot_info 

# zerofill .bss 
    cld 
    mov $bss, %edi 
    mov $bss_end, %ecx 
    sub %edi, %ecx 
    xor %eax, %eax 
    rep stosb 

# create pagetable for identity mapping lower 2 megabytes 
# make minimal page table entries 
    .set pml4_entry, (pdpt + 0x03) 
    .set pdpt_entry, (pd + 0x03) 
    .set pd_entry, 0b10000011 
    movl $pml4_entry, pml4 
    movl $pdpt_entry, pdpt 
    movl $pd_entry, pd 

# setup long mode 
# load global descriptor table 
    lgdt (gdtr) 

# enable Physical Address Extension (PAE) 
    mov %cr4, %eax 
    bts $5, %eax 
    mov %eax, %cr4 

# set up page table 
    mov $kernel_pagetable, %eax 
    mov %eax, %cr3 

# set up long mode 
    .set EFER_MSR_ADDRESS, 0xC0000080 
    mov $EFER_MSR_ADDRESS, %ecx 
    rdmsr 
    bts $8, %eax 
    wrmsr 

# enable paging 
    mov %cr0, %eax 
    bts $31, %eax 
    mov %eax, %cr0 

# long jump to set code segment 
    ljmp $code_segment, $longmode_start 

    .code64 
longmode_start: 
# data segment selector to all data segments 
    mov $data_segment, %bx 
    mov %bx, %ds 
    mov %bx, %es 
    mov %bx, %fs 
    mov %bx, %gs 

# null segment selector to ss 
    mov $null_segment, %bx 
    mov %bx, %ss 

# set up kernel stack 
    mov $kernel_stack_end, %rsp 
    push $0  # debugger backtrace stops here 

# call kmain 
    mov multiboot_magic, %edi 
    mov multiboot_info, %esi 
    call kmain 

# hang the computer 
    cli 
hang: 
    hlt 
    jmp hang 

これはmultiboot.Sです:

.set MULTIBOOT_PAGE_ALIGN, 1 << 0 
    .set MULTIBOOT_MEM_INFO, 1 << 1 
    .set MULTIBOOT_AOUT_KLUDGE, 1 << 16 
    .set MULTIBOOT_MAGIC, 0x1BADB002 
    .set MULTIBOOT_FLAGS, MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEM_INFO | MULTIBOOT_AOUT_KLUDGE 
    .set MULTIBOOT_CHECKSUM, -(MULTIBOOT_MAGIC + MULTIBOOT_FLAGS) 

    .section .mboot 
    .align 4 

    .global multiboot_header 

multiboot_header: 
    .int MULTIBOOT_MAGIC 
    .int MULTIBOOT_FLAGS 
    .int MULTIBOOT_CHECKSUM 
    .int multiboot_header 
    .int text 
    .int data_end 
    .int kernel_end 
    .int start 

そして、これが私のlinker.ldです:

OUTPUT_FORMAT("elf64-x86-64") 
OUTPUT_ARCH(i386:x86-64) 
ENTRY(start) 

phys = 0x0100000; 

SECTIONS 
{ 
    . = phys; 
    kernel_start = .; 

    .text ALIGN(4096) : AT(ADDR(.text)) 
    { 
     text = .; 
     *(.mboot) /* Put Multiboot header section in the beginning of .text section */ 
     *(.text) 
     *(.rodata) 
     text_end = .; 
    } 

    .data ALIGN(4096) : AT(ADDR(.data)) 
    { 
     data = .; 
     *(.data) 
     data_end = .; 
    } 

    .bss ALIGN(4096) : AT(ADDR(.bss)) 
    { 
     bss = .; 
     *(.bss) 
     bss_end = .; 
    } 

    kernel_end = .; 
} 

すべてこのコードは、次のコマンドを使用してcompilledとリンクされている:
はコンパイル...

x86_64-elf-gcc -ffreestanding -mcmodel=large -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -c <file> -o <object-file> 

およびリンク:

x86_64-elf-gcc -ffreestanding -T linker.ld multiboot.o start.o main.o -o kernel.bin -nostdlib -lgcc 

をこのコマンドは中osdev.comによって提案されていますチュートリアルhttp://wiki.osdev.org/Creating_a_64-bit_kernelがすべてコンパイルされ、x86_64アーキテクチャのgccクロスコンパイラを使用してリンクされています。

+0

カーネルに書き込む前に、デバッガの使い方を知りたいかもしれません。低レベルのものについては、私はボッシュをお勧めします。 – Jester

+0

@Jester Okuch、今すぐbochsを見てみよう – Rottenheimer2

+0

あなたのBochsのバージョンが64ビットのサポートを持っていない場合は、ソースからのそのようなサポートでビルドすることができます。 OS Dev WikiにはBochsに関する詳細と、x86-64対応バージョンのビルド方法があります。http://wiki.osdev.org/Bochs#Compiling_Bochs_from_Source –

答えて

4

-kernelパラメータを使用する場合、QEMUはELF64実行可能ファイルをサポートしません。 GRUB2のようなマルチブート互換ローダでカーネルを起動する必要があります。残念ながら、マルチブートヘッダをmultiboot2 compliantに変更する必要があります。あなたは、あなたのことをmultiboot.Sファイルを置き換えることができます。

.section .mboot 
.code32 
.align 8 

    # constants for multiboot2 header: 
    .set MAGIC2, 0xe85250d6 
    .set ARCH2,  0     # i386 protected mode 
    .set CHECKSUM2, (-(MAGIC2 + ARCH2 + (mboot2_end - mboot2_start)) & 0xffffffff) 

    /* multiboot2 header */ 
mboot2_start: 
    .long MAGIC2 
    .long ARCH2 
    .long mboot2_end - mboot2_start 
    .long CHECKSUM2 
    .int 0       # type 
    .int 0       # flags 
    .int 8       # size 
mboot2_end: 

あなたはそれをあなたが前にやったのと同じ方法をコンパイルします。キャッチがあります - このヘッダは、あなたはおそらく、あなたがリンクする際に4KBのページを指定する必要がありますファイルの最初の8キロバイトを超えて押されて終了しないことを確認するために:

x86_64-elf-gcc -z max-page-size=0x1000 -ffreestanding -T linker.ld \ 
    multiboot.o start.o main.o -o kernel.bin -nostdlib -lgcc 

の追加は、 -z max-page-size=0x1000は、最大ページサイズを4kbに強制します。

+0

これは、忍耐と答えのおかげで今動作します。 – Rottenheimer2

+0

もう一度、ありがとう! – Rottenheimer2

+0

私は最後に1回掃除しました(うまくいけば)。私はもともとオンラインで見つけたヘッダーを取って、それがうまくいくことがわかった。私はそれを基本に単純化しました。私は不要なものを完全に削除し、構造上のバグを修正しました。 –

関連する問題