mirror of
				https://github.com/cfenollosa/os-tutorial.git
				synced 2025-06-13 12:54:24 +00:00 
			
		
		
		
	lessons 8, 9, 10, entering 32-bit mode
This commit is contained in:
		
							parent
							
								
									085510f3c8
								
							
						
					
					
						commit
						afa376d2b6
					
				
							
								
								
									
										26
									
								
								08-32bit-print/32bit-print.asm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								08-32bit-print/32bit-print.asm
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								08-32bit-print/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								08-32bit-print/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								09-32bit-gdt/32bit-gdt.asm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								09-32bit-gdt/32bit-gdt.asm
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								09-32bit-gdt/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								09-32bit-gdt/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								10-32bit-enter/32bit-main.asm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								10-32bit-enter/32bit-main.asm
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								10-32bit-enter/32bit-switch.asm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								10-32bit-enter/32bit-switch.asm
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								10-32bit-enter/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								10-32bit-enter/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user