Переглянути джерело

lessons 8, 9, 10, entering 32-bit mode

Carlos Fenollosa 10 роки тому
батько
коміт
afa376d2b6

+ 26 - 0
08-32bit-print/32bit-print.asm

@@ -0,0 +1,26 @@
+[bits 32] ; using 32-bit protected mode
+
+; this is how constants are defined
+VIDEO_MEMORY equ 0xb8000
+WHITE_OB_BLACK equ 0x0f ; the color byte for each character
+
+print_string_pm:
+    pusha
+    mov edx, VIDEO_MEMORY
+
+print_string_pm_loop:
+    mov al, [ebx] ; [ebx] is the address of our character
+    mov ah, WHITE_OB_BLACK
+
+    cmp al, 0 ; check if end of string
+    je print_string_pm_done
+
+    mov [edx], ax ; store character + attribute in video memory
+    add ebx, 1 ; next char
+    add edx, 2 ; next video memory position
+
+    jmp print_string_pm_loop
+
+print_string_pm_done:
+    popa
+    ret

+ 29 - 0
08-32bit-print/README.md

@@ -0,0 +1,29 @@
+*Concepts you may want to Google beforehand: 32-bit protected mode, VGA, video 
+memory*
+
+**Goal: Print on the screen when on 32-bit protected mode**
+
+32-bit mode allows us to use 32 bit registers and memory addressing
+, protected memory, virtual memory and other advangades, but we will lose
+BIOS interrupts and we'll need to code the GDT (more on this later)
+
+In this lesson we will write a print string routine by directly manipulating
+the VGA video memory instead of calling `int 0x10`. The VGA memory starts
+at address `0xb8000` and it has a text mode which is useful to avoid
+manipulating direct pixels.
+
+The formula for accessing a specific character on the 80x25 grid is:
+
+`0xb8000 + 2 * (row * 80 + col)`
+
+That is, every character uses 2 bytes (one for the ASCII, another for 
+color and such), and we see that the structure of the memory concatenates
+rows.
+
+Open `32bit-print.asm` to see the code. It will always print the string
+on the top left of the screen, but soon we'll write higher level routines
+to replace it.
+
+Unfortunately we cannot yet call this routine from the bootloader, because
+we still don't know how to write the GDT and enter protected mode. Once
+you have understood the code, jump to the next lesson.

+ 35 - 0
09-32bit-gdt/32bit-gdt.asm

@@ -0,0 +1,35 @@
+gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps
+    ; the GDT starts with a null 8-byte
+    dd 0x0 ; 4 byte
+    dd 0x0 ; 4 byte
+
+; GDT for code segment. base = 0x00000000, length = 0xfffff
+; for flags, refer to os-dev.pdf document, page 36
+gdt_code: 
+    dw 0xffff    ; segment length, bits 0-15
+    dw 0x0       ; segment base, bits 0-15
+    db 0x0       ; segment base, bits 16-23
+    db 10011010b ; flags (8 bits)
+    db 11001111b ; flags (4 bits) + segment length, bits 16-19
+    db 0x0       ; segment base, bits 24-31
+
+; GDT for data segment. base and length identical to code segment
+; some flags changed, again, refer to os-dev.pdf
+gdt_data:
+    dw 0xffff
+    dw 0x0
+    db 0x0
+    db 10010010b
+    db 11001111b
+    db 0x0
+
+gdt_end:
+
+; GDT descriptor
+gdt_descriptor:
+    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
+    dd gdt_start ; address (32 bit)
+
+; define some constants for later use
+CODE_SEG equ gdt_code - gdt_start
+DATA_SEG equ gdt_data - gdt_start

+ 31 - 0
09-32bit-gdt/README.md

@@ -0,0 +1,31 @@
+*Concepts you may want to Google beforehand: GDT*
+
+**Goals: program the GDT**
+
+Remember segmentation from lesson 6? The offset was left shifted
+to address an extra level of indirection.
+
+In 32-bit mode, segmentation works differently. Now, the offset becomes an
+index to a segment descriptor (SD) in the GDT. This descriptor defines
+the base address (32 bits), the size (20 bits) and some flags, like
+readonly, permissions, etc. To add confusion, the data structures are split,
+so open the os-dev.pdf file and check out the figure on page 34 or the 
+Wikipedia page for the GDT.
+
+The easiest way to program the GDT is to define two segments, one for code
+and another for data. These can overlap which means there is no memory protection,
+but it's good enough to boot, we'll fix this later with a higher language.
+
+As a curiosity, the first GDT entry must be `0x00` to make sure that the
+programmer didn't make any mistakes managing addresses.
+
+Furthermore, since the CPU needs to know how long the GDT is, we'll use
+a meta structure called the "GDT descriptor" with the size (16b) and address
+(32b) of our actual GDT.
+
+Let's directly jump to the GDT code in assembly. Again, to understand
+all the segment flags, refer to the os-dev.pdf document. The theory for
+this lesson is quite complex.
+
+In the next lesson we will make the switch to 32-bit protected mode
+and test our code from these lessons.

+ 27 - 0
10-32bit-enter/32bit-main.asm

@@ -0,0 +1,27 @@
+[org 0x7c00] ; bootloader offset
+    mov bp, 0x9000 ; set the stack
+    mov sp, bp
+
+    mov bx, MSG_REAL_MODE
+    call print ; This will be written after the BIOS messages
+
+    call switch_to_pm
+    jmp $ ; this will actually never be executed
+
+%include "../05-bootsector-functions-strings/boot_sect_print.asm"
+%include "../09-32bit-gdt/32bit-gdt.asm"
+%include "../08-32bit-print/32bit-print.asm"
+%include "32bit-switch.asm"
+
+[bits 32]
+BEGIN_PM: ; after the switch we will get here
+    mov ebx, MSG_PROT_MODE
+    call print_string_pm ; Note that this will be written at the top left corner
+    jmp $
+
+MSG_REAL_MODE db "Started in 16-bit real mode", 0
+MSG_PROT_MODE db "Loaded 32-bit protected mode", 0
+
+; bootsector
+times 510-($-$$) db 0
+dw 0xaa55

+ 22 - 0
10-32bit-enter/32bit-switch.asm

@@ -0,0 +1,22 @@
+[bits 16]
+switch_to_pm:
+    cli ; 1. disable interrupts
+    lgdt [gdt_descriptor] ; 2. load the GDT descriptor
+    mov eax, cr0
+    or eax, 0x1 ; 3. set 32-bit mode bit in cr0
+    mov cr0, eax
+    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment
+
+[bits 32]
+init_pm: ; we are now using 32-bit instructions
+    mov ax, DATA_SEG ; 5. update the segment registers
+    mov ds, ax
+    mov ss, ax
+    mov es, ax
+    mov fs, ax
+    mov gs, ax
+
+    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
+    mov esp, ebp
+
+    call BEGIN_PM ; 7. Call a well-known label with useful code

+ 23 - 0
10-32bit-enter/README.md

@@ -0,0 +1,23 @@
+*Concepts you may want to Google beforehand: interrupts, pipelining*
+
+**Goal: Enter 32-bit protected mode and test our code from previous lessons**
+
+To jump into 32-bit mode:
+
+1. Disable interrupts
+2. Load our GDT
+3. Set a bit on the CPU control register `cr0`
+4. Flush the CPU pipeline by issuing a carefully crafted far jump
+5. Update all the segment registers
+6. Update the stack
+7. Call to a well-known label which contains the first useful code in 32 bits
+
+We will encapsulate this process on the file `32bit-switch.asm`. Open it
+and take a look at the code.
+
+After entering 32-bit mode, we will call `BEGIN_PM` which is the entry point
+for our actual useful code (e.g. kernel code, etc). You can read the code
+at `32bit-main.asm`. Compile and run this last file and you will see the two 
+messages on the screen.
+
+Congratulations! Our next step will be to write a simple kernel