как загрузить ядро ​​из загрузчика?

avatar
ZTECH
8 августа 2021 в 20:30
207
2
-1

Я пытаюсь создать операционную систему. Я только что завершил загрузчик, однако у меня возникла проблема с загрузкой ядра.

Boot.asm:

section .boot
bits 16
global boot
boot:
    mov ax, 0x2401
    int 0x15

    mov ax, 0x3
    int 0x10

    mov [disk],dl

    mov ah, 0x2    ;read sectors
    mov al, 6      ;sectors to read
    mov ch, 0      ;cylinder idx
    mov dh, 0      ;head idx
    mov cl, 2      ;sector idx
    mov dl, [disk] ;disk idx
    mov bx, copy_target;target pointer
    int 0x13
    cli
    lgdt [gdt_pointer]
    mov eax, cr0
    or eax,0x1
    mov cr0, eax
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp CODE_SEG:boot2
gdt_start:
    dq 0x0
gdt_code:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:
gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start
disk:
    db 0x0
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

times 510 - ($-$$) db 0
dw 0xaa55
copy_target:
bits 32
    hello: db "Hello more than 512 bytes world!!",0
boot2:
    mov esi,hello
    mov ebx,0xb8000
.loop:
    lodsb
    or al,al
    jz halt
    or eax,0x0F00
    mov word [ebx], ax
    add ebx,2
    jmp .loop
halt:
    mov esp,kernel_stack_top
    extern kzos
    call kzos
    cli
    hlt

section .bss
align 4
kernel_stack_bottom: equ $
    resb 16384 ; 16 KB
kernel_stack_top:

kzos.cpp

extern "C" void kzos()
{
    const short color = 0x0F00;
    const char* hello = "Kernel test!";
    short* vga = (short*)0xb8000;
    for (int i = 0; i<16;++i)
        vga[i+80] = color | hello[i];
}

Ошибка, которую я получаю, связана с "extern kzos", ошибка гласит: "boot.asm:77: ошибка: формат двоичного вывода не поддерживает внешние ссылки"

Я попытался добавить " extern kzos.cpp ", но затем получил "boot.asm:77: error: symbol `kmain' undefined" если я добавлю «.cpp» в функцию вызова, я получаю «boot4.asm: 77: ошибка: формат двоичного вывода не поддерживает внешние ссылки»

Я использую Nasm для компиляции в bin и qemu для его запуска.

Источник
Yakk - Adam Nevraumont
8 августа 2021 в 20:45
1

Пожалуйста, опишите вашу цепочку инструментов. Какой ассемблер, какой компилятор и т.д.

Jester
8 августа 2021 в 20:47
1

Вы не показали, как вы строите вещи, но если вы не связываете их вместе, вам нужно найти способ указать адрес kzos самостоятельно. Например. вы можете поместить указатель или переход в начало ядра.

Lala5th
8 августа 2021 в 20:49
1

Отвечает ли это на ваш вопрос? nasm: ошибка: формат двоичного вывода не поддерживает внешние ссылки

Peter Cordes
8 августа 2021 в 21:03
0

Как сделать ядро ​​для моего загрузчика? есть рабочий пример вызова C в защищенном режиме из загрузчика, который запускается в реальном режиме. Вы не указали, какие именно команды вы пытаетесь запустить, которые вызывают эти ошибки ссылок.

ZTECH
8 августа 2021 в 21:08
0

Я использую Linux Mint, Nasm и Qemu. Я использую следующую команду: nasm -fbin boot.asm -o boot.bin.

Lala5th
8 августа 2021 в 21:11
0

Как упоминалось в другом сообщении, двоичный формат не имеет таблицы символов. Из того, что я видел, вам нужно нормально скомпилировать в объектный файл (эльф), а затем использовать компоновщик для создания необработанного двоичного файла.

Ответы (2)

avatar
Brendan
8 августа 2021 в 22:17
2

Я только что закончил загрузчик, ...

Нет. По крайней мере, половина кода загрузчика существует для обработки ошибок, которые «не должны» возникать (такие как проверка того, не удалось ли BIOS включить A20 или если BIOS сообщает, что возникла проблема при чтении данных с диска) и каким-то образом обрабатывать эти случаи. - как минимум, предоставляя информацию о пользователе, которая может помочь ему решить проблему (и определить, является ли это неисправным оборудованием или проблемой с тем, как была установлена ​​ОС или ...), чтобы пользователь не застревал в раздумьях, почему его весь компьютер непригоден для использования (и чтобы вы не застряли с сообщением об ошибке, в котором говорится, что «не загружается» без полезной информации).

как загрузить ядро ​​из загрузчика?

Вы можете выбрать, где находится ядро ​​(его расположение на диске и его размер):

a) Поместите ядро ​​в какую-нибудь обычную файловую систему, и загрузчик должен поддерживать эту файловую систему (например, найти правильную запись каталога и получить расположение данных файла из записи каталога файла). Обратите внимание, что это сложно (например, у вас будет много обработки ошибок, если структура файловой системы повреждена и т. д.)

b) Поместите ядро ​​в какую-либо специально разработанную файловую систему или в какую-то специально предназначенную "специальную/зарезервированную область" обычной файловой системы. Это может быть так же просто, как таблица структур «смещения и длины», хранящаяся в первом секторе, где файл ядра всегда является первой записью в этой таблице.

c) Включите двоичный файл ядра непосредственно в загрузчик, используя что-то вроде директивы incbin в NASM. В этом случае вы можете использовать метки для определения размера файла ядра, например:

kernel_start:
    incbin 'kernel.bin'
kernel_end:

В этом случае вы можете определить, где на диске находится ядро, по тому, где на диске находился загрузчик, и вычислить количество секторов (например, (kernel_end - kernel_start + SECTOR_SIZE-1)/SECTOR_SIZE). Конечно, это ужасно негибко (например, вы не можете легко обновить ядро ​​без сборки и последующей переустановки загрузчика).

После того, как вы определили расположение и размер ядра на диске; вам нужно загрузить его в память где-то. Обратите внимание, что это может зависеть от того, какой формат исполняемого файла вы выбрали для ядра; и может включать загрузку заголовков исполняемого файла и их анализ, чтобы выяснить, какие части файла должны находиться в памяти (и настроить такие вещи, как «.bss», которых нет в файле).

avatar
Errorist
8 августа 2021 в 22:03
0

формат вывода — bin, плоский формат, то есть

не поддерживает перемещение

таким образом, местоположение/адрес call kzos теряется после рендеринга кода
любая утилита связывания не сможет найти call kzos' в порядке
связать его с новым адресом extern "C" void kzos() из kzos.cpp