Внешний код C не выполняется из двухэтапного загрузчика x86? - PullRequest
3 голосов
/ 11 марта 2020

Я хочу сделать игрушку, но я застрял на стадии загрузчика. Я просто хотел написать очень простой загрузчик, который загружает мой основной C код, мне не очень нравится сборка x86, что происходит когда-то в загрузчике второго этапа, после перехода в защищенный / 32-битный режим я вызываю мой Функция kmain() c, использующая ключевое слово extern, похоже go приводит к некоторой бесконечной перезагрузке l oop ИЛИ qemu вылетает с этой ошибкой "Попытка выполнить код вне ОЗУ или ПЗУ в 0x000a0000"

У меня также есть несколько основанных на x86 ассемблерных функций для печати текста на экране, они хорошо работают, если используются, но вызов функции extern c вызывает вышеупомянутые ошибки

«поток» выглядит следующим образом bootloader.asm (загрузчик первой ступени) -> Stage2.asm (загрузчик второй ступени) -> kmain. cpp GDT, стек, A20 настраиваются в Stage2.asm для запуска защищенного режима / 32-разрядного режима, а затем - внешнего Вызывается функция kmain. Файл kmain () c ++ пытается записать только один символ с цветом в буфер VGA

Я на хосте UBuntu 18.04, используя nasm для сборки g cc для компоновки и c компиляции и qemu для тестирования этой так называемой ОС

Это команды, которые я использую для сборки и запуска

nasm -f elf bootload.asm -o bootload.o
nasm -f elf Stage2.asm -o stage2.o
gcc -m32  stage2.o bootload.o kmain.cpp -o kernel.bin  -g -nostdlib -ffreestanding -std=c++11 -mno-red-zone -fno-exceptions -nostdlib -fno-rtti -Wall -Wextra -Werror -T linker.ld
qemu-system-i386 -fda kernel.bin

Это my bootloader.asm

[bits 16]
section .boot
global boot

boot:
    hello: db "Hello world!",0
    mov si,hello 
    mov ah,0x0e 
.loop:
    lodsb
    or al,al 
    jz diskboot 
    int 0x10 
    jmp .loop

diskboot:
    mov ax,0x800   
    mov es,ax         

    xor bx,bx   
    mov ah,0x2 
    mov al,0x1  
    mov ch,0x0  
    mov cl,0x2  
    mov dh,0x0  
    int 0x13
    jmp 0x800:0 

Stage2.asm


section .kernel
bits    16


jmp main                ; go to start

;*******************************************************
;   Preprocessor directives
;*******************************************************

%include "stdio.inc"            ; basic i/o routines
%include "Gdt.inc"          ; Gdt routines
%include "A20.inc"          ; A20 enabling

;*******************************************************
;   Data Section
;*******************************************************

LoadingMsg db 0x0D, 0x0A, "Searching for Operating System...", 0x00


main:


    cli             ; clear interrupts
    xor ax, ax          ; null segments
    mov ds, ax
    mov es, ax
    mov ax, 0x9000      ; stack begins at 0x9000-0xffff
    mov ss, ax
    mov sp, 0xFFFF
    sti             ; enable interrupts



    call    InstallGDT      ; install our GDT



    call    EnableA20_KKbrd_Out


    mov si, LoadingMsg
    call    Puts16


EnterStage3:

    cli             ; clear interrupts
    mov eax, cr0        ; set bit 0 in cr0--enter pmode
    or  eax, 1
    mov cr0, eax

    jmp CODE_DESC:Stage3    ; far jump to fix CS


bits 32

Stage3:


    mov     ax, DATA_DESC   
    mov     ds, ax
    mov     ss, ax
    mov     es, ax
    mov     fs, ax
    mov     gs, ax

    mov ebp, 0x90000
    mov esp, ebp


    extern kmain
    call kmain
    cli
    hlt


msg db  0x0A, "<[ OS Development Series Tutorial 10 ]>",  0x0A, 0

A20.in c


;********************************************
;   Enable A20 address line
;
;   OS Development Series
;********************************************

%ifndef __A20_INC_67343546FDCC56AAB872_INCLUDED__
%define __A20_INC_67343546FDCC56AAB872_INCLUDED__

bits    16

;----------------------------------------------
; Enables a20 line through keyboard controller
;----------------------------------------------

EnableA20_KKbrd:

    cli
    push    ax
    mov al, 0xdd    ; send enable a20 address line command to controller
    out 0x64, al
    pop ax
    ret

;--------------------------------------------
; Enables a20 line through output port
;--------------------------------------------

EnableA20_KKbrd_Out:

    cli
    pusha

        call    wait_input
        mov     al,0xAD
        out     0x64,al     ; disable keyboard
        call    wait_input

        mov     al,0xD0
        out     0x64,al     ; tell controller to read output port
        call    wait_output

        in      al,0x60
        push    eax     ; get output port data and store it
        call    wait_input

        mov     al,0xD1
        out     0x64,al     ; tell controller to write output port
        call    wait_input

        pop     eax
        or      al,2        ; set bit 1 (enable a20)
        out     0x60,al     ; write out data back to the output port

        call    wait_input
        mov     al,0xAE     ; enable keyboard
        out     0x64,al

        call    wait_input
    popa
        sti
        ret

    ; wait for input buffer to be clear

wait_input:
        in      al,0x64
        test    al,2
        jnz     wait_input
        ret

    ; wait for output buffer to be clear

wait_output:
        in      al,0x64
        test    al,1
        jz      wait_output
        ret

;--------------------------------------
; Enables a20 line through bios
;--------------------------------------

EnableA20_Bios:
    pusha
    mov ax, 0x2401
    int 0x15
    popa
    ret

;-------------------------------------------------
; Enables a20 line through system control port A
;-------------------------------------------------

EnableA20_SysControlA:
    push    ax
    mov al, 2
    out 0x92, al
    pop ax
    ret

%endif

Gdt.in c



;*************************************************
;   Gdt.inc
;       -GDT Routines
;
;   OS Development Series
;*************************************************

%ifndef __GDT_INC_67343546FDCC56AAB872_INCLUDED__
%define __GDT_INC_67343546FDCC56AAB872_INCLUDED__

bits    16

;*******************************************
; InstallGDT()
;   - Install our GDT
;*******************************************

InstallGDT:

    cli                  ; clear interrupts
    pusha                ; save registers
    lgdt    [toc]        ; load GDT into GDTR
    sti                  ; enable interrupts
    popa                 ; restore registers
    ret                  ; All done!

;*******************************************
; Global Descriptor Table (GDT)
;*******************************************

gdt_data: 
    dd 0                ; null descriptor
    dd 0 

; gdt code:             ; code descriptor
    dw 0FFFFh           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10011010b        ; access
    db 11001111b        ; granularity
    db 0                ; base high

; gdt data:             ; data descriptor
    dw 0FFFFh           ; limit low (Same as code)10:56 AM 7/8/2007
    dw 0                ; base low
    db 0                ; base middle
    db 10010010b        ; access
    db 11001111b        ; granularity
    db 0                ; base high

end_of_gdt:
toc: 
    dw end_of_gdt - gdt_data - 1    ; limit (Size of GDT)
    dd gdt_data             ; base of GDT

; give the descriptor offsets names

%define NULL_DESC 0
%define CODE_DESC 0x8
%define DATA_DESC 0x10

%endif ;__GDT_INC_67343546FDCC56AAB872_INCLUDED__

linker.ld

ENTRY(boot)
OUTPUT_FORMAT("binary")

SECTIONS {
  . = 0x7c00;
  .boot :
  {
    *(.boot)
  }

  . = 0x7dfe;
  .sig : {
     SHORT(0xaa55);
  }

  . = 0x8000;
  .kernel : AT(0x7e00)  /* place immediately after the boot sector */
  {
    *(.kernel)
    *(.text)
    *(.rodata)
    *(.data)
    *(.bss)
    *(COMMON)
  }
  kernel_sectors = (SIZEOF(.kernel) + 511) / 512;

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

kmain. cpp

extern "C" void kmain()
{

unsigned char* vga = (unsigned char*) 0xb8000;
   vga[0] = 'S'; 
   vga[1] = 0x09; 
   for(;;); 
}

РЕДАКТИРОВАТЬ: добавлено stdio.in c

%ifndef __STDIO_INC_67343546FDCC56AAB872_INCLUDED__
%define __STDIO_INC_67343546FDCC56AAB872_INCLUDED__


;==========================================================
;
;    16 Bit Real Mode Routines
;==========================================================


;************************************************;
;   Puts16 ()
;       -Prints a null terminated string
;   DS=>SI: 0 terminated string
;************************************************;

bits    16

Puts16:
        pusha               ; save registers
.Loop1:
        lodsb               ; load next byte from string from SI to AL
        or  al, al          ; Does AL=0?
        jz  Puts16Done      ; Yep, null terminator found-bail out
        mov ah, 0eh         ; Nope-Print the character
        int 10h         ; invoke BIOS
        jmp .Loop1          ; Repeat until null terminator found
Puts16Done:
        popa                ; restore registers
        ret             ; we are done, so return


;==========================================================
;
;    32 Bit Protected Mode Routines
;==========================================================

bits 32

%define     VIDMEM  0xB8000         ; video memory
%define     COLS    80          ; width and height of screen
%define     LINES   25
%define     CHAR_ATTRIB 63          ; character attribute (White text on light blue background)

_CurX db 0                  ; current x/y location
_CurY db 0

;**************************************************;
;   Putch32 ()
;       - Prints a character to screen
;   BL => Character to print
;**************************************************;

Putch32:

    pusha               ; save registers
    mov edi, VIDMEM     ; get pointer to video memory

    ;-------------------------------;
    ;   Get current position    ;
    ;-------------------------------;

    xor eax, eax        ; clear eax

        ;--------------------------------
        ; Remember: currentPos = x + y * COLS! x and y are in _CurX and _CurY.
        ; Because there are two bytes per character, COLS=number of characters in a line.
        ; We have to multiply this by 2 to get number of bytes per line. This is the screen width,
        ; so multiply screen with * _CurY to get current line
        ;--------------------------------

        mov ecx, COLS*2     ; Mode 7 has 2 bytes per char, so its COLS*2 bytes per line
        mov al, byte [_CurY]    ; get y pos
        mul ecx         ; multiply y*COLS
        push    eax         ; save eax--the multiplication

        ;--------------------------------
        ; Now y * screen width is in eax. Now, just add _CurX. But, again remember that _CurX is relative
        ; to the current character count, not byte count. Because there are two bytes per character, we
        ; have to multiply _CurX by 2 first, then add it to our screen width * y.
        ;--------------------------------

        mov al, byte [_CurX]    ; multiply _CurX by 2 because it is 2 bytes per char
        mov cl, 2
        mul cl
        pop ecx         ; pop y*COLS result
        add eax, ecx

        ;-------------------------------
        ; Now eax contains the offset address to draw the character at, so just add it to the base address
        ; of video memory (Stored in edi)
        ;-------------------------------

        xor ecx, ecx
        add edi, eax        ; add it to the base address

    ;-------------------------------;
    ;   Watch for new line          ;
    ;-------------------------------;

    cmp bl, 0x0A        ; is it a newline character?
    je  .Row            ; yep--go to next row

    ;-------------------------------;
    ;   Print a character           ;
    ;-------------------------------;

    mov dl, bl          ; Get character
    mov dh, CHAR_ATTRIB     ; the character attribute
    mov word [edi], dx      ; write to video display

    ;-------------------------------;
    ;   Update next position        ;
    ;-------------------------------;

    inc byte [_CurX]        ; go to next character
;   cmp byte [_CurX], COLS      ; are we at the end of the line?
;   je  .Row            ; yep-go to next row
    jmp .done           ; nope, bail out

    ;-------------------------------;
    ;   Go to next row              ;
    ;-------------------------------;

.Row:
    mov byte [_CurX], 0     ; go back to col 0
    inc byte [_CurY]        ; go to next row

    ;-------------------------------;
    ;   Restore registers & return  ;
    ;-------------------------------;

.done:
    popa                ; restore registers and return
    ret

;**************************************************;
;   Puts32 ()
;       - Prints a null terminated string
;   parm\ EBX = address of string to print
;**************************************************;

Puts32:

    ;-------------------------------;
    ;   Store registers             ;
    ;-------------------------------;

    pusha               ; save registers
    push    ebx         ; copy the string address
    pop edi

.loop:

    ;-------------------------------;
    ;   Get character               ;
    ;-------------------------------;

    mov bl, byte [edi]      ; get next character
    cmp bl, 0           ; is it 0 (Null terminator)?
    je  .done           ; yep-bail out

    ;-------------------------------;
    ;   Print the character         ;
    ;-------------------------------;

    call    Putch32         ; Nope-print it out

    ;-------------------------------;
    ;   Go to next character        ;
    ;-------------------------------;

    inc edi         ; go to next character
    jmp .loop

.done:

    ;-------------------------------;
    ;   Update hardware cursor      ;
    ;-------------------------------;

    ; Its more efficiant to update the cursor after displaying
    ; the complete string because direct VGA is slow

    mov bh, byte [_CurY]    ; get current position
    mov bl, byte [_CurX]
    call    MovCur          ; update cursor

    popa                ; restore registers, and return
    ret

;**************************************************;
;   MoveCur ()
;       - Update hardware cursor
;   parm/ bh = Y pos
;   parm/ bl = x pos
;**************************************************;

bits 32

MovCur:

    pusha               ; save registers (aren't you getting tired of this comment?)

    ;-------------------------------;
    ;   Get current position        ;
    ;-------------------------------;

    ; Here, _CurX and _CurY are relitave to the current position on screen, not in memory.
    ; That is, we don't need to worry about the byte alignment we do when displaying characters,
    ; so just follow the forumla: location = _CurX + _CurY * COLS

    xor eax, eax
    mov ecx, COLS
    mov al, bh          ; get y pos
    mul ecx         ; multiply y*COLS
    add al, bl          ; Now add x
    mov ebx, eax

    ;--------------------------------------;
    ;   Set low byte index to VGA register ;
    ;--------------------------------------;

    mov al, 0x0f
    mov dx, 0x03D4
    out dx, al

    mov al, bl
    mov dx, 0x03D5
    out dx, al          ; low byte

    ;---------------------------------------;
    ;   Set high byte index to VGA register ;
    ;---------------------------------------;

    xor eax, eax

    mov al, 0x0e
    mov dx, 0x03D4
    out dx, al

    mov al, bh
    mov dx, 0x03D5
    out dx, al          ; high byte

    popa
    ret

;**************************************************;
;   ClrScr32 ()
;       - Clears screen
;**************************************************;

bits 32

ClrScr32:

    pusha
    cld
    mov edi, VIDMEM
    mov cx, 2000
    mov ah, CHAR_ATTRIB
    mov al, ' ' 
    rep stosw

    mov byte [_CurX], 0
    mov byte [_CurY], 0
    popa
    ret

;**************************************************;
;   GotoXY ()
;       - Set current X/Y location
;   parm\   AL=X position
;   parm\   AH=Y position
;**************************************************;

bits 32

GotoXY:
    pusha
    mov [_CurX], al     ; just set the current position
    mov [_CurY], ah
    popa
    ret




%endif ;__STDIO_INC_67343546FDCC56AAB872_INCLUDED__

1 Ответ

1 голос
/ 14 марта 2020

stdio.inc имел функции, которые уже записывали в VGA кадровый буфер с использованием ассемблера x86 в 32-битном защищенном режиме, эти функции конфликтовали с моим кодом C, который также пытался записать в кадровый буфер. удаление stdio.in c и всех ссылок на его функции решило проблему, которую я теперь могу записать в VGA-буфер, и на экране отображается цветной текст с использованием кода C.

...