2016-07-02 4 views
3

나는 내 자신의 커널을 구현하고 있는데, 막혔다. 커널을 상반부 가상 주소로로드하려고합니다. 나는 ID가 낮은 1M의 RAM을 매핑하는 정체성 문제를 해결했습니다. 페이징 초기화를 처리하기 위해 커널의 물리적 주소로 재배치 된 init 섹션을 만들었습니다. 커널의 가상 오프셋은 0xc0000000입니다. 이것은 제 링커 스크립트입니다 :페이징을 사용할 때 QEMU 트리플 폴트

OUTPUT_FORMAT(elf32-i386) 
ENTRY(start) 

KERNEL_VIRTUAL_OFFSET = 0xC0000000; 

SECTIONS 
{ 
    . = 1M; 
    kernel_start = .; 

    start_init = .; 
    .init ALIGN(4K) : 
          { *(.multiboot); 
          *(.init); 
          *(.tables); 
          } 
    end_init = .; 

    . += KERNEL_VIRTUAL_OFFSET; 

    kernel_high_half_start = .; 
    .text ALIGN(4K) : AT(ADDR(.text) - KERNEL_VIRTUAL_OFFSET) 
         {*(.text) } 
    .data ALIGN(4K) : AT(ADDR(.data) - KERNEL_VIRTUAL_OFFSET) 
         { *(.data) } 
    .rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_VIRTUAL_OFFSET) 
         { *(.rodata) } 
    .bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_VIRTUAL_OFFSET) 
         { *(.bss) } 
    kernel_high_half_end = .; 

    kernel_end = . - KERNEL_VIRTUAL_OFFSET; 
} 

여기 내 입장이 있습니다. GRUB을 부트 로더로 사용하고 있습니다.

its 32 
    section .multiboot 
    ;grub bootloader header 
      align 4 
      dd 0x1BADB002   ;magic 
      dd 0x00     ;flags 
      dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero 

    ; Declarations 
    global start 
    extern kmain 
    extern paging_init 
    extern kernel_page_directory 

    section .init 

    enable_paging: 
     mov eax, kernel_page_directory 
     mov cr3, eax 
     mov eax, cr0 
     or eax, 0x80000000 
     mov cr0, eax ; ***** PAGING ENABLED HERE ***** 
     ret 

    start: 
     cli   ;block interrupts 
     mov esp, init_stack 
     call paging_init 
     call enable_paging 

     ;mov eax, 0xb8000 
     ;mov byte[eax], 'h' 
     ;mov byte[eax+1], 0x7 

     ; Now high half kernel is mapped to the page directory 
     mov esp, stack_space ;set stack pointer 
     push ebx ; grub boot info 
     call kmain 

    loop: 
     hlt   ;halt the CPU 
     jmp loop 

    resb 4096; 4KB small stack for my init section. 
    init_stack: 

    section .bss 
    resb 8192 ;8KB for stack 
    stack_space: 

가 여기에 페이지 테이블과 커널의 페이지 디렉토리를 채우고 내 코드입니다 : 그것은 성공적으로 부팅되고 때문에 init 섹션의 내 진입 점으로 이동합니다. 당신이 볼 수 있듯이, 전체 코드는 이전 문제를 방지하기 위해 init 섹션으로 연결되어 있습니다 :

page_table_t kernel_page_directory[PAGE_DIR_SIZE] 
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0}; 

page_pointer_t kernel_page_tables[PAGE_TABLE_SIZE] 
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0}; 

page_pointer_t identity_page_table[PAGE_TABLE_SIZE] 
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0}; 

/* Identity map the low 1M 
* In early boot stage. 
*/ 
static void __attribute__((section(".init"))) map_identity() 
{ 
    //map bios 
    unsigned int current_page = 0; 
    for(int i = 0; i < BIOS_PAGE_TABLE_ENTRIES; i++, current_page += PAGE_SIZE) 
    { 
     identity_page_table[i] = (current_page) | 0x3; 
    } 

    //map init 
    current_page = INIT_START; 
    for(int i = INIT_START >> 12 & 0x3FF; 
     i < ((INIT_START >> 12 & 0x3FF) + (INIT_SIZE/PAGE_SIZE)); 
     i++, current_page += PAGE_SIZE) 
    { 
     identity_page_table[i] = (current_page) | 0x3; 
    } 

    kernel_page_directory[0] = ((unsigned long)(identity_page_table)) | 0x3; 
} 

/* Map the kernel memory to its page directory, 
* **in early boot stage. 
* We don't need to map the init section, we don't need it anymore. 
*/ 

__attribute__((section(".init"))) static void map_kernel_memory() 
{ 
    //Identity map the init section 
    //Start at 1MB i.e. its page aligned. 
    unsigned int start_index = 256; 
    unsigned long current_page = KERNEL_START; 

    for(int i = start_index; 
     i < start_index + (KERNEL_SIZE/PAGE_SIZE) + 1; 
     i++, current_page += PAGE_SIZE) 
    { 
     kernel_page_tables[i] = current_page | 0x3; 
    } 

    kernel_page_directory[KERNEL_DIRECTORY_ENTRY] = ((unsigned long)kernel_page_tables) | 0x3; 
} 

__attribute__((section(".init"))) void paging_init() 
{ 
    map_identity(); 

    map_kernel_memory(); 
} 

나는 정확한 어셈블리 명령어를 가리려고 노력을하지만 잘못 내 커널 작업을하고 나는 그것이 있기 때문에 생각 페이징을 활성화하면 mov cr0, eax입니다. CR3에는 kernel_page_directory 또는 0x3의 주소가 포함되어 있습니다. 페이징을 사용하자마자 QEMU는 응답을 중지하고 시스템이 계속 재부팅됩니다. 화면이 비우고 반복적으로 인쇄됩니다. 왜 이런 일이 일어나고 있는거야? 어떻게 해결할 수 있습니까?

+1

최소한의 완전한 입증 가능한 예제를 제공하는 것이 도움이 될 수 있습니다. 모든 _C_ 파일 (일부분)을 제공하지 않고 사용중인 정의를 제공하지 않았습니다. 나는 어떤 것이 무엇인지 추측 할 수 있지만, 올바르게 정의되었는지는 알 수 없습니다. 누구나 독립적으로 컴파일/어셈블/링크 할 수 있도록 충분한 코드를 제공합니다. 또한 이점 - 컴파일/어셈블/링크하는 데 사용하는 실제 명령은 무엇입니까? 그것도이 같은 질문에서 종종 귀중합니다. –

+1

@MichaelPetch 디버깅이 끔찍한 밤 후에, 나는 해결책을 찾았다. 문제는 내가 직접 GRUB의 GDT를 사용하는 것이 었습니다. 내 추측은 reffering하는 선형 주소 중 하나가 내 페이지 디렉토리에 매핑되지 않았기 때문에 페이지 폴트 또는 정의되지 않은 동작이 발생했습니다. – Delights

+2

[multiboot 사양] (https://www.gnu.org/software/grub/manual/multiboot/multiboot.html)에는 이러한 내용이 있습니다. 관련 인용문 : _GDTR 세그먼트 레지스터가 위에서 설명한대로 설정되어 있어도 'GDTR'이 유효하지 않을 수 있으므로 ** OS 이미지에 세그먼트 레지스터 **가로드되어서는 안됩니다 (** 동일한 값을 다시로드하는 경우에도 마찬가지입니다). ** 자신의 'GDT'를 설정할 때까지. ** _ –

답변

0

페이지 디렉토리의 주소가 페이지 정렬입니까? 각 페이지 (프레임)의 크기는 4KB입니다. 다음과 같은 페이지 디렉토리 구조체를 만드는 것이 좋습니다.

typedef struct page_directory{ 
    page_table_t *tables[1024]; 
    size_t tablesPhysical[1024]; // Physical address of page tables 
    size_t physicalAddr;   // Physical address of `tablesPhysical' 
} page_directory_t; 

따라서 주소는 4KB (0x1000)의 배수 여야합니다. James Molloy's Tutorial 도움을 받으실 수 있습니다.

+0

그는'__attribute __ ((aligned (PAGE_SIZE)))'를 사용합니다. PAGE_SIZE가 4096이라는 나머지 코드에서 유추 할 수 있습니다. 따라서 사용중인 정렬 된 속성은 4k 경계에서 정렬됩니다. 코멘트에서 그는 자신의 문제를 발견했으며 GRUB의 GDT를 사용하는 것과 관련이 있다고 언급했습니다. –