How to write your own Operating System (x86_64): Bootloader, 2nd Stage (long mode)
Πως να γράψετε το δικό σας Λειτουργικό Σύστημα (x86_64): Εκκινητής, 2o Στάδιο (μακρά κατάσταση)
IntroductionΕισαγωγή
In our previous article, we showed how to implement the first stage of the bootloader. Now, let's proceed to the second stage. First of all, in order for the execution to continue to the second stage, we have to remove the relevant comment from the jmp instruction in stage1/bootstage1.asm:
Σε προηγούμενο άρθρο μας, δείξαμε πως υλοποιούμε το πρώτο στάδιο του εκκινητή. Εδώ, θα εξετάσουμε το δεύτερο στάδιο. Πρώτα από όλα, για να προχωρήσει η εκτέλεση στο δεύτερο στάδιο, πρέπει να αφαιρέσουμε το σχετικό σχόλιο από την εντολή jmp στο stage1/bootstage1.asm:
...
; Jump to the entry point of stage 2
jmp Stage2_entrypoint
...
Stage 2 codeΚώδικας του 2ου σταδίου
In the x86_64 computer architecture, long mode is the architecture's intended primary mode of operation, where a 64-bit operating system can access 64-bit instructions and registers. Let's see some things we need in order to enter the long mode:
- We check if the long mode is supported.
- We activate the A20 gate / address line.
- We prepare the memory paging.
- We remap the PIC (Programmable Interrupt Controller).
- Then, we enter the long mode and pass the control to the Kernel.
Let's examine the code for the second stage:
Στην αρχιτεκτονική υπολογιστών x86_64, η προορισμένη κατάσταση λειτουργίας είναι η λεγόμενη μακρά κατάσταση (long mode) όπου ενα 64-bit λειτουργικό σύστημα έχει πρόσβαση στις 64-bit εντολές και τους 64-bit καταχωρητές. Ας δούμε τι χρειάζεται να κάνουμε ώστε να μπούμε στην λειτουργία μακράς κατάστασης:
- Ελέγχουμε αν η λειτουργία μακράς κατάστασης υποστηρίζεται.
- Ενεργοποιούμε την πύλη / γραμμή διεύθυνσης A20.
- Προετοιμάζομε τη σελιδοποίηση της μνήμης.
- Επανακαθορίζουμε τον Προγραμματιζόμενο Ελεγκτή Διακοπών (PIC).
- Τέλος, εισερχόμαστε στη λειτουργία μακράς κατάστασης και δίνουμε τον έλεγχο στον Πυρήνα.
Ας δούμε, λοιπόν, τον κώδικα για το δεύτερο στάδιο:
stage2/bootstage2.asm
BITS 16
;---Initialized data------------------------------------------------------------
stage2_message dw 19
db 'Entering Stage 2...'
longmode_supported_message dw 23
db 'Long mode is supported.'
longmode_not_supported_message dw 27
db 'Long mode is not supported.'
;---Code------------------------------------------------------------------------
Stage2_entrypoint:
; Print 'Entering Stage 2...' message
mov si, stage2_message
call Real_mode_println
; Check if long mode is supported
call Is_longmode_supported
test eax, eax
jz .long_mode_not_supported
mov si, longmode_supported_message
call Real_mode_println
; Enable Gate A20
call Enable_A20
; Prepare paging
call Prepare_paging
; Remap PIC
call Remap_PIC
; Enter long mode
call Enter_long_mode
.long_mode_not_supported:
mov si, longmode_not_supported_message
call Real_mode_println
.halt: hlt ; Infinite loop.
jmp .halt ; (It prevents us from going off in memory and executing junk).
; Include
%include "stage2/a20.asm"
%include "stage2/paging.asm"
%include "stage2/pic.asm"
%include "stage2/longmode.asm"
stage2/a20.asm
When IBM PC AT was released in 1984 (designed around the Intel 80286 microprocessor), for the sake of backward compatibility, a quirk in the 8086 microprocessor architecture (a memory wraparound) had to be replicated. You can read more about this issue here. Thus, the A20 line on the address bus was (and still is in many systems) disabled by default. This means that if we want to gain access to the full memory range, we have to enable the A20 line. There are several different ways (that may or may not be supported) and some of them may cause problems on some computers. Therefore, the recommended method is to try all of them, in the order of least risk, until one works:
- We test if A20 is already enabled. If it is, we don't need to do anything at all.
- We try to enable A20 using the BIOS function (we ignore the returned status).
- We test if A20 is enabled (to see if the BIOS function actually worked or not).
- We try to enable A20 using the keyboard controller method.
- We test if A20 is enabled in a loop with a time-out (as the keyboard controller method may work slowly).
- We try to enable A20 using the "Fast A20" method last.
- We test if A20 is enabled in a loop with a time-out (as the "Fast A20" method may work slowly).
- If none of the above worked, we give up.
Το 1984 κυκλοφόρησε ο IBM PC AT, σχεδιασμένος γύρω από τον μικροεπεξεργαστη 80286 της Intel. Για χάρη της προς τα πίσω συμβατότητας, μια ιδιοτροπία στην αρχιτεκτονική του μικροεπεξεργαστή 8086 (μια κυκλική επικάλυψη στην πρόσβαση της μνήμης) έπρεπε να αναπαραχθεί και σε αυτόν. Μπορείτε να διαβάσετε περισσότερα για αυτό το θέμα εδώ. Η ουσία είναι ότι η γραμμή Α20 στο δίαυλο διευθύνσεων ήταν (και εξακολουθεί να είναι σε πολλά συστήματα) αρχικά απενεργοποιημένη. Αυτό σημαίνει ότι αν θέλουμε να αποκτήσουμε πρόσβαση στο πλήρες εύρος της μνήνης, πρέπει να ενεργοποιήσουμε τη γραμμή Α20. Υπάρχουν αρκετοί διαφορετικοί τρόποι για να το επιτύχουμε αυτό (που μπορει να υποστηρίζονται ή όχι) και κάποιοι από αυτούς μπορεί να δημιουργήσουν προβλήματα σε κάποια συστήματα. Ως εκ τούτου, η συνιστώμενη μέθοδος είναι να δοκιμάσουμε όλους τους τρόπους κατά σειρά αυξανόμενου ρίσκου, μέχρι κάποιος να δουλέψει:
- Ελέγχουμε αν η A20 είναι ήδη ενεργοποιημένη. Αν είναι, δε χρειάζεται να κάνουμε κάτι άλλο.
- Δοκιμάζουμε να ενεργοποιήσουμε την Α20, χρησιμοποιώντας τη λειτουργία του BIOS (αγνοούμε την τιμή που μας γυρίζει).
- Ελέγχουμε αν η A20 ενεργοποιήθηκε (για να δούμε αν η λειτουργία του BIOS δούλεψε στην πράξη ή όχι).
- Δοκιμάζουμε να ενεργοποιήσουμε την Α20, χρησιμοποιώντας τον ελεκτή του πληκτρολογίου.
- Ελέγχουμε αν η A20 ενεργοποιήθηκε σε ένα βρόχο με κάποιο χρονικό τέλος (μιας και η μέθοδος με τον ελεκτή του πληκτρολογίου μπορεί να πάρει κάποιο χρόνο για να δουλέψει).
- Δοκιμάζουμε να ενεργοποιήσουμε την Α20, χρησιμοποιώντας τη μέθοδο "Fast A20" τελευταία.
- Ελέγχουμε αν η A20 ενεργοποιήθηκε σε ένα βρόχο με κάποιο χρονικό τέλος (μιας και η μέθοδος "Fast A20" μπορεί να πάρει κάποιο χρόνο για να δουλέψει).
- Αν δε δούλεψε κανένα από τα παραπάνω, τα παρατάμε.
BITS 16
;---Initialized data------------------------------------------------------------
a20_enabled_message dw 15
db 'A20 is enabled.'
a20_disabled_message dw 16
db 'A20 is disabled.'
a20_trying_bios dw 34
db 'Trying to enable A20 using BIOS...'
a20_trying_keyb dw 49
db 'Trying to enable A20 using Keyboard Controller...'
a20_trying_io92 dw 40
db 'Trying to enable A20 using IO port 92...'
;---Code------------------------------------------------------------------------
Enable_A20:
call Check_A20 ; Check if A20 is already enabled.
test ax, ax
jnz .end
; Try to enable A20 using BIOS.
mov si, a20_trying_bios
call Real_mode_println
call Enable_A20_using_BIOS
call Check_A20 ; Check if A20 is enabled.
test ax, ax
jnz .end
; Try to enable A20 using eEyboard Controller.
mov si, a20_trying_keyb
call Real_mode_println
call Enable_A20_using_Keyboard_Controller
call Check_A20 ; Check if A20 is enabled.
test ax, ax
jnz .end
; Try to enable A20 using IO port 92h (Fast A20 method).
mov si, a20_trying_io92
call Real_mode_println
call Enable_A20_using_Keyboard_Controller
call Check_A20 ; Check if A20 is enabled.
test ax, ax
jnz .end
.halt: hlt
jmp .halt ; Infinite loop.
.end:
ret
Check_A20:
;********************************************************************;
; Check the status of the A20 line ;
;********************************************************************;
call Real_mode_check_A20
test ax, ax
jnz .a20_enabled
mov si, a20_disabled_message
call Real_mode_println
ret
.a20_enabled:
mov si, a20_enabled_message
call Real_mode_println
ret
Real_mode_check_A20:
;**************************************************************************;
; Check the status of the A20 line (in real mode) ;
;--------------------------------------------------------------------------;
; Returns: ax = 0 if the a20 line is disabled (memory wraps around) ;
; ax = 1 if the a20 line is enabled (memory does not wrap around) ;
;**************************************************************************;
pushf
push ds
push es
push di
push si
cli ; clear interrupts
xor ax, ax ; ax = 0
mov es, ax ; es = 0
not ax ; ax = 0xFFFF
mov ds, ax ; ds = 0xFFFF
mov di, 0x0500 ; 0500 and 0510 are chosen since they are guaranteed to be free
mov si, 0x0510 ; for use at any point of time after BIOS initialization.
; save the original values found at these addresses.
mov dl, byte [es:di]
push dx
mov dl, byte [ds:si]
push dx
mov byte [es:di], 0x00 ; [es:di] is 0:0500
mov byte [ds:si], 0xFF ; [ds:si] is FFFF:0510
cmp byte [es:di], 0xFF ; if the A20 line is disabled, [es:di] will contain 0xFF
; (as the write to [ds:si] really occured to 00500).
mov ax, 0 ; A20 disabled ([es:di] equal to 0xFF).
je .a20_disabled
mov ax, 1 ; A20 enabled.
.a20_disabled:
; restore original values
pop dx
mov byte [ds:si], dl
pop dx
mov byte [es:di], dl
pop si
pop di
pop es
pop ds
popf
sti ; Enable interrupts.
ret
Enable_A20_using_BIOS:
;*************************************************************;
; Try to enable A20 gate using the BIOS (int 15h, ax = 2401h) ;
;-------------------------------------------------------------;
; Returns: ax = 0 (Failure) ;
; ax = 1 (Success) ;
;*************************************************************;
mov ax,2403h ; Query A20 gate Support (later PS/2s systems)
int 15h
jb .failure ; INT 15h is not supported
cmp ah, 0
jnz .failure ; INT 15h is not supported
mov ax, 2402h ; Get A20 gate Status
int 15h
jb .failure ; Couldn't get status
cmp ah, 0
jnz .failure ; Couldn't get status
cmp al, 1
jz .success ; A20 is already activated
mov ax, 2401h ; Enable A20 gate
int 15h
jb .failure ; Couldn't enable the A20 gate
cmp ah, 0
jnz .failure ; Couldn't enable the A20 gate
.success:
mov ax, 1
ret
.failure:
mov ax, 0
ret
Disable_A20_using_BIOS:
;**************************************************************;
; Try to disable A20 gate using the BIOS (int 15h, ax = 2400h) ;
;**************************************************************;
mov ax, 2400h
int 15h
ret
Enable_A20_using_Keyboard_Controller:
;******************************************************************;
; Try to enable A20 line using the Keyboard Controller (chip 8042) ;
;------------------------------------------------------------------;
; Returns: ax = 0 (Failure) ;
; ax = 1 (Success) ;
;******************************************************************;
cli ; Clear interrupts.
call a20wait
mov al, 0xAD ; Disable keyboard.
out 0x64, al
call a20wait
mov al, 0xD0 ; Read from input.
out 0x64, al
call a20wait2
in al,0x60
push eax
call a20wait
mov al, 0xD1 ; Write to output.
out 0x64, al
call a20wait
pop eax
or al, 2
out 0x60, al
call a20wait
mov al, 0xAE ; Enable keyboard.
out 0x64, al
call a20wait
sti ; Enables interrupts.
ret
a20wait:
in al, 0x64
test al, 2
jnz a20wait
ret
a20wait2:
in al, 0x64
test al, 1
jz a20wait2
ret
;*********************************************************************;
; Enable A20 Line via IO port 92h (Fast A20 method) ;
;---------------------------------------------------------------------;
; This method is quite dangerous because it may cause conflicts with ;
; some hardware devices forcing the system to halt. ;
;=====================================================================;
; Bits of port 92h ;
;---------------------------------------------------------------------;
; Bit 0 - Setting to 1 causes a fast reset ;
; Bit 1 - 0: disable A20, 1: enable A20 ;
; Bit 2 - Manufacturer defined ;
; Bit 3 - power on password bytes. 0: accessible, 1: inaccessible ;
; Bits 4-5 - Manufacturer defined ;
; Bits 6-7 - 00: HDD activity LED off, 01 or any value is "on" ;
;*********************************************************************;
Enable_A20_using_IO_port_92:
in al, 0x92 ; Read from port 0x92
test al, 2 ; Check if bit 1 (i.e. the 2nd bit) is set.
jnz .end ; If bit 1 (i.e. the 2nd bit) is already set don't do anything.
or al, 2 ; Activate bit 1 (i.e. the 2nd bit).
and al, 0xFE ; Make sure bit 0 is 0 (it causes a fast reset).
out 0x92, al ; Write to port 0x92
.end:
ret
stage2/paging.asm
Here is the code that sets the memory paging:
Εδώ είναι ο κώδικας που καθορίζει τη σελιδοποίηση της μνήμης:
BITS 16
;---Define----------------------------------------------------------------------
%define PAGE_PRESENT (1 << 0)
%define PAGE_WRITE (1 << 1)
%define CODE_SEG 0x0008
%define PAGING_DATA 0x9000
;---Initialized data------------------------------------------------------------
;****************************************************************************************;
; Global Descriptor Table (GDT) ;
;****************************************************************************************;
; The Global Descriptor Table (GDT) is a data structure used by x86-family processors ;
; (starting with the 80286) in order to define the characteristics of the various memory ;
; areas (segments) used during program execution, including the base address, the size, ;
; and access privileges like executability and writability. ;
;****************************************************************************************;
GDT:
.Null:
dq 0x0000000000000000 ; Null Descriptor (should be present).
.Code:
dq 0x00209A0000000000 ; 64-bit code descriptor (exec/read).
dq 0x0000920000000000 ; 64-bit data descriptor (read/write).
ALIGN 4
dw 0 ; Padding (to make the "address of the GDT" field aligned on a 4-byte boundary).
.Pointer:
dw $ - GDT - 1 ; 16-bit Size (Limit) of GDT.
dd GDT ; 32-bit Base Address of GDT. (CPU will zero extend to 64-bit)
;---Code------------------------------------------------------------------------
Prepare_paging:
;*******************************************************************************************;
; Prepare paging ;
;-------------------------------------------------------------------------------------------;
; ES:EDI Should point to a valid page-aligned 16KiB buffer, for the PML4, PDPT, PD and a PT.;
; SS:ESP Should point to memory that can be used as a small (1 uint32_t) stack. ;
;*******************************************************************************************;
mov edi, PAGING_DATA ; Point edi to a free space to create the paging structures.
; Zero out the 16KiB buffer. Since we are doing a rep stosd, count should be bytes/4.
push di ; REP STOSD alters DI.
mov ecx, 0x1000
xor eax, eax
cld
rep stosd
pop di ; Get DI back.
; Build the Page Map Level 4. ES:DI points to the Page Map Level 4 table.
lea eax, [es:di + 0x1000] ; EAX = Address of the Page Directory Pointer Table.
or eax, PAGE_PRESENT | PAGE_WRITE ; OR EAX with the flags (present flag, writable flag).
mov [es:di], eax ; Store the value of EAX as the first PML4E.
; Build the Page Directory Pointer Table.
lea eax, [es:di + 0x2000] ; Put the address of the Page Directory in to EAX.
or eax, PAGE_PRESENT | PAGE_WRITE ; OR EAX with the flags (present flag, writable flag).
mov [es:di + 0x1000], eax ; Store the value of EAX as the first PDPTE.
; Build the Page Directory.
lea eax, [es:di + 0x3000] ; Put the address of the Page Table in to EAX.
or eax, PAGE_PRESENT | PAGE_WRITE ; OR EAX with the flags (present flag, writable flag).
mov [es:di + 0x2000], eax ; Store to value of EAX as the first PDE.
push di ; Save DI for the time being.
lea di, [di + 0x3000] ; Point DI to the page table.
mov eax, PAGE_PRESENT | PAGE_WRITE ; Move the flags into EAX - and point it to 0x0000.
; Build the Page Table.
.LoopPageTable:
mov [es:di], eax
add eax, 0x1000
add di, 8
cmp eax, 0x200000 ; If we did all 2MiB, end.
jb .LoopPageTable
pop di ; Restore DI.
ret
stage2/pic.asm
The Programmable Interrupt Controller (PIC) is one of the most important chips in the x86 architecture. Its function is to manage hardware interrupts and send them to the appropriate system interrupts. This allows the system to respond to devices without the need for polling them all the time. In protected mode and long mode, the Interrupt Requests (IRQs) 0-15 are in conflict with the CPU exceptions which reserve the first 32 positions (0-31 or 0x00-0x1F). Thus, prior to switching to the long mode, we have to change the PIC's offsets (a.k.a. remapping the PIC) so that IRQs use non-reserved vectors:
O Προγραμματιζόμενος Ελεγκτής Διακοπών (Programmable Interrupt Controller, PIC) είναι ένα από τα πιο σημαντικά ολοκληρωμένα κυκλώματα στην αρχιτεκτονικη x86. Η λειτούργια του είναι να διαχειρίζεται τις διακοπές υλικού (hardware interrupts) και να τις στέλνει στις κατάλληλες διακοπές συστήματος. Αυτό επιτρέπει στο σύστημα να ανταποκρίνεται στις συσκευές χωρίς την ανάγκη να τις βολιδοσκοπεί περιοδικά (polling) όλη την ώρα. Στην προστατευμένη κατάσταση λειτουργίας (protected mode) και στην μακρά κατάσταση λειτουργίας (long mode), οι αιτήσεις διακοπών (Interrupt Requests, IRQs) 0-15 συγκρούονται με τις εξαιρέσεις του επεξεργαστή (CPU exceptions) που δεσμεύουν τις πρώτες 32 θέσεις (0-31 ή 0x00-0x1F). Ως εκ τούτου, πριν τη μετάβαση στην λειτουργία μακράς κατάστασης, πρέπει να μετατοπίσουμε τα IRQs σε μη δεσμευμένες θέσεις στον PIC (αυτή η διαδικασία είναι γνωστή και ως "remapping the PIC"):
; PIC (Programmable Interrupt Controller)
BITS 16
;---Constants----------------------------------------------------------------------
PIC1_COMMAND equ 0x20 ; Command port of 1st PIC
PIC1_DATA equ 0x21 ; Data port of 1st PIC
PIC2_COMMAND equ 0xA0 ; Command port of 2nd PIC
PIC2_DATA equ 0xA1 ; Data port of 2nd PIC
PIC_EOI equ 0x20 ; EOI (End of interrupt) command (= 0x20)
ICW1_ICW4 equ 0x01 ; Initialization Command Word 4 is needed
ICW1_SINGLE equ 0x02 ; Single mode (0: Cascade mode)
ICW1_INTERVAL4 equ 0x04 ; Call address interval 4 (0: 8)
ICW1_LEVEL equ 0x08 ; Level triggered mode (0: Edge mode)
ICW1_INIT equ 0x10 ; Initialization - required!
ICW4_8086 equ 0x01 ; 8086/88 mode (0: MCS-80/85 mode)
ICW4_AUTO_EOI equ 0x02 ; Auto End Of Interrupt (0: Normal EOI)
ICW4_BUF_SLAVE equ 0x08 ; Buffered mode/slave
ICW4_BUF_MASTER equ 0x0C ; Buffered mode/master
ICW4_SFNM equ 0x10 ; Special Fully Nested Mode
;---Code---------------------------------------------------------------------------
Remap_PIC:
;***************************************************************************;
; In protected / long mode, the IRQs 0-15 conflict with the CPU exceptions ;
; (which are reserved up until 0x1F). It is thus recommended to change the ;
; PIC's offsets (remapping the PIC) so that IRQs use non-reserved vectors. ;
; A common choice is to move them to the beginning of the available range: ;
; IRQs 0..15 -> INT 0x20..0x2F (30..47). For that, we need to set the 1st ;
; PIC's offset to 0x20 (32) and the 2nd's to 0x28 (40). ;
;***************************************************************************;
push ax
; Disable IRQs
mov al, 0xFF ; Out 0xFF to 0xA1 and 0x21 to mask/disable all IRQs.
out PIC1_DATA, al
out PIC2_DATA, al
nop
nop
; Remap PIC
mov al, ICW1_INIT | ICW1_ICW4 ; ICW1: Send initialization command (= 0x11) to both PICs
out PIC1_COMMAND, al
out PIC2_COMMAND, al
mov al, 0x20 ; ICW2: Set vector offset of 1st PIC to 0x20 (i.e. IRQ0 => INT 32)
out PIC1_DATA, al
mov al, 0x28 ; ICW2: Set vector offset of 2nd PIC to 0x28 (i.e. IRQ8 => INT 40)
out PIC2_DATA, al
mov al, 4 ; ICW3: tell 1st PIC that there is a 2nd PIC at IRQ2 (= 00000100)
out PIC1_DATA, al
mov al, 2 ; ICW3: tell 2nd PIC its "cascade" identity (= 00000010)
out PIC2_DATA, al
mov al, ICW4_8086 ; ICW4: Set mode to 8086/88 mode
out PIC1_DATA, al
out PIC2_DATA, al
mov al, 0xFF ; OCW1: We mask all interrupts (until we set a proper IDT in Kernel)
out PIC1_DATA, al
out PIC2_DATA, al
pop ax
ret
stage2/longmode.asm
Here is the code for checking if the long mode is supported and the code for entering the long mode:
Εδώ βρίσκεται ο κώδικας που έχει να κάνει με τον έλεγχο υποστήριξης και τη μετάβαση στην λειτουργία μακράς κατάστασης (long mode):
BITS 16
;---Code------------------------------------------------------------------------
Is_longmode_supported:
;********************************************************************;
; Check if Long mode is supported ;
;--------------------------------------------------------------------;
; Returns: eax = 0 if Long mode is NOT supported, else non-zero. ;
;********************************************************************;
mov eax, 0x80000000 ; Test if extended processor info in available.
cpuid
cmp eax, 0x80000001
jb .not_supported
mov eax, 0x80000001 ; After calling CPUID with EAX = 0x80000001,
cpuid ; all AMD64 compliant processors have the longmode-capable-bit
test edx, (1 << 29) ; (bit 29) turned on in the EDX (extended feature flags).
jz .not_supported ; If it's not set, there is no long mode.
ret
.not_supported:
xor eax, eax
ret
Enter_long_mode:
;********************************************************************;
; Enter long mode ;
;********************************************************************;
mov edi, PAGING_DATA; Point edi at the PAGING_DATA.
mov eax, 10100000b ; Set the PAE and PGE bit.
mov cr4, eax
mov edx, edi ; Point CR3 at the PML4.
mov cr3, edx
mov ecx, 0xC0000080 ; Read from the EFER MSR.
rdmsr
or eax, 0x00000100 ; Set the LME bit.
wrmsr
mov ebx, cr0 ; Activate long mode
or ebx, 0x80000001 ; by enabling paging and protection simultaneously.
mov cr0, ebx
lgdt [GDT.Pointer] ; Load GDT.Pointer.
jmp CODE_SEG:Kernel ; Load CS with 64 bit segment and flush the instruction cache.
kernel/kernel.asm
Here is some dummy code that write a message directly into the video RAM, just to test if our bootloader is functional:
Εδώ έχουμε έναν απλό κώδικα, ο οποίος γράφει έναν μήνυμα απευθείας στην περιοχή της μνήμης των γραφικών, απλά για να δούμε αν ο εκκινητή μας είναι λειτουργικός:
BITS 64 ; We have entered the long mode! :)
;---Define----------------------------------------------------------------------
%define DATA_SEG 0x0010
%define VRAM 0xB8000
;---Code------------------------------------------------------------------------
Kernel:
;********************************************************************;
; Just some dummy code for now ;
;********************************************************************;
; Set all segments registers to DATA_SEG
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Set EDI to point to Video RAM (0xB8000)
mov edi, VRAM
; Print "Hello World!"
mov rax, 0x1F6C1F6C1F651F48
mov [edi], rax
mov rax, 0x1F6F1F571F201F6F
mov [edi + 8], rax
mov rax, 0x1F211F641F6C1F72
mov [edi + 16], rax
.halt: hlt
jmp .halt ; Infinite loop.
Running our codeΕκτέλεση του κώδικά μας
We can test our code very easily using an emulator like QEMU:
Μπορούμε να δοκιμάσουμε τον κώδικά μας πολύ εύκολα, χρησιμοποιώντας έναν εξομοιωτή όπως ο QEMU:
$ make
$ qemu-system-x86_64 -drive format=raw,file=os.bin
Full codeΠλήρης κώδικας
You can download the full code from here: bootloader code
Μπορείτε να κατεβάσετε τον πλήρη κώδικα απο εδώ: κώδικας εκκινητή