Инструкция после включения подкачки не отображается для выполнения - PullRequest
0 голосов
/ 05 октября 2018

Когда мне нужно настроить регистр EIP, программа не переходит в правильное положение.Я ожидаю, что jmp *%ecx переместится в нужное место в настройке EIP памяти до 0xC0100000 (метка: StartInHigherHalf), используя LEA.Я не думаю, что kmain необходимо, потому что проблема заключается в том, прежде чем он вызывается.В любом случае я опубликую его.

Я пытался отладить его с флагом -d cpu в QEMU, и перед переходом (заблокированным с помощью HLT) говорится, что ECX не загружен функцией LEA,Возможно ли, чтобы инструкция LEA не выполнялась?Почему это может случиться?Как я могу это исправить?

Boot.S:

.set ALIGN,    1<<0                                       
.set MEMINFO,  1<<1                               
.set FLAGS,    ALIGN | MEMINFO                         
.set MAGIC,    0x1BADB002                      
.set CHECKSUM, -(MAGIC + FLAGS)            

.set KERNEL_VIRTUAL_BASE, 0xC0000000                       
.set KERNEL_PAGE_NUMBER, (KERNEL_VIRTUAL_BASE >> 22)

.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

.section .data
.align 0x1000
BootPageDirectory:

.quad 0x00000083

.rept KERNEL_PAGE_NUMBER - 1
.quad 0        
.endr

.quad 0x00000083
.rept 0x400 - KERNEL_PAGE_NUMBER - 1
.quad 0                    
.endr

.set STACKSIZE, 0x4000

.global __start__
.set __start__, (setup)
setup: 
    mov $(BootPageDirectory - KERNEL_VIRTUAL_BASE), %ecx
    mov %ecx, %cr3 

    mov %cr4, %ecx
    or $0x00000010, %ecx 
    mov %ecx, %cr4    

    mov %cr0, %ecx
    or $0x80000000, %ecx
    mov %ecx, %cr0

    lea StartInHigherHalf, %ecx
    jmp *%ecx      

StartInHigherHalf:
    movl $0, (BootPageDirectory)
    invlpg (0)

    mov $(stack + STACKSIZE), %esp                 
    push %eax                                        

    push %ebx

    call _init

    call kmain

    cli
1:  hlt
    jmp 1

.section .bss
.align 32

.lcomm stack, STACKSIZE    
.global gdtFlush            
.extern gp               

gdtFlush:
    lgdt (gp)

    mov $0x10, %eax
    mov %eax, %ds               
    mov %eax, %es
    mov %eax, %gs
    mov %eax, %fs
    mov %eax, %ss

    ljmp $0x08, $setcs                              

setcs:
    ret

linker.ld:

ENTRY(__start__)
OUTPUT_FORMAT(elf32-i386)

SECTIONS {
    . = 0xC0100000;

    .text  ALIGN(0x1000) : AT(ADDR(.text) - 0xC0000000) {
        *(.multiboot)
        *(.text)
    }
    .rodata ALIGN(0x1000) : AT(ADDR(.rodata) - 0xC0000000) {
        *(.rodata*)
    } 
    .data ALIGN(0x1000) : AT(ADDR(.data) - 0xC0000000) {
        *(.data)
    }
    .bss ALIGN(0x1000) : AT(ADDR(.bss) - 0xC0000000) {
        _sbss = .;
        *(COMMON)
        *(.bss)
        _ebss = .;
    }
}

kmain.c:

#include <kernel/tty.h>
#include <kernel/log.h>
#include <kernel/stack-protector.h>
#include <kernel/gdt.h>

__attribute__ ((noreturn));
    void kmain(void) {
    gdtInstall();

    initializeTerminal();
    char c;
    char *buffer = &c;
    char *start = buffer;

    char str[] = "Hello, kernel World!";

    atomicallyLog(1, 1, str, buffer);
    kprintf(start, 1);
}

1 Ответ

0 голосов
/ 06 октября 2018

Если мы на мгновение предположим, что ваш код на самом деле достигает lea StartInHigherHalf, %ecx, и не думаем, что он фактически выполнил эту инструкцию - наиболее очевидная проблема связана с подкачкой.Инструкция LEA оказывается первой инструкцией, выполняемой после установки бита поискового вызова.Если разбиение по страницам неверно, то, вероятно, следующая инструкция даст сбой.

Просматривая каталог вашей страницы, я заметил, что вы строите его, используя .quad (8-байтовый тип) вместо .int (4-байтовый тип).Каждая запись в каталоге страниц составляет 4 байта, а не 8. Это, вероятно, является основной причиной ваших проблем.Вы можете избежать макроса .rept, просто используя директиву .fill:

.fill repeat , size , value

результат, размер и значение являются абсолютными выражениями.Это испускает повторные копии байтов размера.Повтор может быть ноль или больше.Размер может быть равен нулю или больше, но если он больше 8, то считается, что он имеет значение 8, совместимое с ассемблерами других людей.Содержимое каждого повторного байта берется из 8-байтового числа.4 байта высшего порядка равны нулю.4 байта самого низкого порядка представляют собой значение, представленное в порядке байтов целого числа на компьютере, для которого выполняется сборка.Каждый размер байта в повторении берется из младшего байта размера этого числа.Опять же, это странное поведение совместимо с ассемблерами других людей.

размер и значение необязательны.Если вторая запятая и значение отсутствуют, значение принимается равным нулю.Если первая запятая и следующие токены отсутствуют, предполагается, что размер равен 1.

Ваш каталог страницы загрузки может быть записан так:

.align 0x1000
BootPageDirectory:    
.int 0x00000083    
.fill KERNEL_PAGE_NUMBER - 1, 4, 0    
.int 0x00000083
.fill 0x400 - KERNEL_PAGE_NUMBER - 1, 4, 0

.rept также работает:

.align 0x1000
BootPageDirectory:
.int 0x00000083    
.rept KERNEL_PAGE_NUMBER - 1
.int 0
.endr
.int 0x00000083
.rept 0x400 - KERNEL_PAGE_NUMBER - 1
.int 0
.endr

Другие предложения

Ваш код является вариантом ядра OSDev Higher Half x86 Bare Bones .Основное различие заключается в том, что вы конвертировали из NASM в GNU Assembler.Я написал о серьезном недостатке этого кода на форуме OSDev .Как это структурировано, все адреса генерируются, предполагая, что все находится на более высокой половине, а не разделяет нижнюю половину, которая находится в 0x100000 от того, что находится в верхней половине в 0xC0100000.Этот дизайн приводит к использованию Mulitboot способом, который на самом деле не определен в спецификации Multiboot.

Вы можете решить эту проблему, используя скрипт компоновщика, чтобы разделить их и поместить соответствующие разделы в boot.S.Сценарий компоновщика (linker.ld) может выглядеть так:

ENTRY(setup)
OUTPUT_FORMAT(elf32-i386)

KERNEL_VIRTUAL_BASE = 0xC0000000;

SECTIONS {
   /* The multiboot data and code will exist in low memory
      starting at 0x100000 */

   . = 0x00100000;

   .lowerhalf ALIGN(0x1000) : {
       *(.lowerhalf.data)
       *(.lowerhalf.text)
   }

   /* The kernel will live at 3GB + 1MB in the virtual
      address space, which will be mapped to 1MB in the
      physical address space. */

   . += KERNEL_VIRTUAL_BASE;
   .text ALIGN(0x1000) : AT(ADDR(.text) - KERNEL_VIRTUAL_BASE) {
       *(.text)
   }

   .data ALIGN (0x1000) : AT(ADDR(.data) - KERNEL_VIRTUAL_BASE) {
       *(.data)
       *(.rodata*)
   }

   .bss ALIGN (0x1000) : AT(ADDR(.bss) - KERNEL_VIRTUAL_BASE) {
       _sbss = .;
       *(COMMON)
       *(.bss)
       _ebss = .;
   }

   /DISCARD/ : {
       *(.eh_frame);
       *(.comment*);
   }
}

Ваш файл boot.S может быть записан так:

.set ALIGN,    1<<0
.set MEMINFO,  1<<1
.set FLAGS,    ALIGN | MEMINFO
.set MAGIC,    0x1BADB002
.set CHECKSUM, -(MAGIC + FLAGS)

.set KERNEL_VIRTUAL_BASE, 0xC0000000
.set KERNEL_PAGE_NUMBER, (KERNEL_VIRTUAL_BASE >> 22)

.section .lowerhalf.data,"aw",@progbits
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

.align 0x1000
BootPageDirectory:
.int 0x00000083
.fill KERNEL_PAGE_NUMBER - 1, 4, 0    
.int 0x00000083
.fill 0x400 - KERNEL_PAGE_NUMBER - 1, 4, 0

.set STACKSIZE, 0x4000

.section .lowerhalf.text,"axw",@progbits
.global setup
setup:
    mov $BootPageDirectory, %ecx
    mov %ecx, %cr3

    mov %cr4, %ecx
    or $0x00000010, %ecx
    mov %ecx, %cr4

    mov %cr0, %ecx
    or $0x80000000, %ecx
    mov %ecx, %cr0

    jmp StartInHigherHalf

.section .text
StartInHigherHalf:
    movl $0, (BootPageDirectory)
    invlpg (0)

    mov $(stack + STACKSIZE), %esp
    push %eax

    push %ebx

    #call _init

    call kmain

    cli
1:  hlt
    jmp 1

/*
.global gdtFlush
.extern gp

gdtFlush:
    lgdt (gp)

    mov $0x10, %eax
    mov %eax, %ds
    mov %eax, %es
    mov %eax, %gs
    mov %eax, %fs
    mov %eax, %ss

    ljmp $0x08, $setcs

setcs:
    ret
*/

.section .bss
.align 32

.lcomm stack, STACKSIZE

А ядро ​​ничего не делать (kernel.c) можетвыглядит так:

volatile unsigned short int *const video_mem = (unsigned short int *)0xc00b8000;
void kmain(void) {
    /* print MDP in upper left of display using white on magenta */
    video_mem[0] = (0x57 << 8) | 'M';
    video_mem[1] = (0x57 << 8) | 'D';
    video_mem[2] = (0x57 << 8) | 'P';

    while (1) __asm__ ("hlt");
}

Вы можете создать исполняемый файл ELF с помощью:

i686-elf-gcc -c -g -m32 boot.S -o boot.o
i686-elf-gcc -c -g -m32 -O3 kernel.c -o kernel.o -ffreestanding -std=gnu99 \
    -mno-red-zone -fno-exceptions -Wall -Wextra

i686-elf-gcc -nostdlib -Wl,--build-id=none -T linker.ld boot.o kernel.o -lgcc -o kernel.elf

Комментарии:

  • Я предпочитаю использовать кросс-компилятор GCC, хотя вытакже можете использовать свой собственный компилятор хоста.
  • Файл kernel.elf может быть загружен GRUB (в файле ISO) или напрямую с опцией QEMU -kernel.
  • Я упростил JMP с помощьюпрыгнув на ярлык StartInHigherHalf напрямую.Вы все еще можете использовать метод LEA / JMP, но он ничего не получает.
  • Вы ошибочно поместили gdtFlush в раздел .bss.Его нужно перенести в раздел .text.Я сделал это в коде, но я также прокомментировал это, чтобы ядро ​​не работало с подкачкой страниц.Вам нужно будет добавить его обратно для работы с вашей кодовой базой.
  • Я временно закомментировал call _init, чтобы это ядро ​​не работало.Вам нужно будет удалить комментарий, чтобы интегрировать его в свою кодовую базу.
...