Если мы на мгновение предположим, что ваш код на самом деле достигает 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
, чтобы это ядро не работало.Вам нужно будет удалить комментарий, чтобы интегрировать его в свою кодовую базу.