📜 ⬆️ ⬇️

Slightly more about loading self-made OS - write a bootloader

Not so long ago, I decided to study IA-32 architecture a little better. And what is best for memorization? Of course practice. But programming in the OS, we are unlikely to get the lowest level of access to hardware without interference. Therefore, for these purposes, we will write our own similarity of the operating system. That is, simply speaking, we will execute our code immediately after loading the BIOS.
The first problem that anyone who wants to program at a low level will face is how to load your code?

Introduction


Usually in the BIOS there is a list of devices from which it is trying to boot, going through one by one. This list usually consists of a drive, a CD drive, and a hard disk. Booting from a floppy disk and a CD is almost the same — for compatibility, a floppy disk image is placed in the boot area of ​​the disk, which is then copied into memory and acts as a virtual drive. And since booting from a floppy is the easiest, and we will use it.
After the BIOS detects all the devices and performs everything necessary for itself, it loads the first sector of the diskette into memory at address 0000: 7C00 and transfers control there. This is where we encounter the first problem - the size of the sector on a floppy disk is only 512 bytes in size, and we have to meet these limits in order to load the rest of the code. If this still seems to you a lot, I will say that for compatibility, some 60 more of them are spent on service purposes. Of course, you can throw them out, but then the diskette may not be visible on the systems, and copying files to it will be difficult.
To simplify, add this data later. At once I will make a reservation that all the above code will be given in the syntax of FASM.
So let's start with the simplest, getting control and text output.
  1. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  2. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  3. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  4. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  5. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  6. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  7. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  8. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  9. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  10. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  11. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  12. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  13. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  14. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  15. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  16. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  17. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  18. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  19. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  20. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  21. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  22. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  23. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  24. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  25. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  26. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  27. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa
  28. Use16 org 0x7C00 start : cli ; mov ax , cs ; mov ds , ax mov es , ax mov ss , ax mov sp , 0x7C00 ; .. , mov ax , 0xB800 mov gs , ax ; mov si , msg call k_puts hlt ; jmp $ ; k_puts : lodsb test al , al jz . end_str mov ah , 0x0E mov bl , 0x07 ; int 0x10 jmp k_puts . end_str ret msg db 'Hello world' , 0x0d , 0x0a , 0 times 510 - ( $ - $$ ) db 0 db 0x55 , 0xaa


If you think that we will finish on this, as in hundreds of other examples, then I will disappoint you, and maybe I will please you - our downloader will search the disk for the file and download it.
To begin, I will tell you about the layout of a floppy under FAT12.
The first sector is reserved for service data - a BIOS parameter block (BPB), as well as a boot code.

BIOS Parameter Block


BPB in our case will look like this:
 jmp start;  jump to our code
	 db 0
	 BS_OEMName db 'MicLib';  any text
	 BPB_BytsPerSec dw 0x200;  byte in the sector
	 BPB_SecPerClus db 1;  sectors in a cluster
	 BPB_RsvdSecCnt dw 1;  number of reserved sectors
	 BPB_NumFATs db 2;  number of FAT tables
	 BPB_RootEntCnt dw 0x00E0;  number of entries in the root tree
	 BPB_TotSec16 dw 0x0B40
	 BPB_Media db 0xF0
	 BPB_FATSz16 dw 9;  FAT size in sectors
	 BPB_SecPerTrk dw 0x12;  sectors on track
	 BPB_NumHeads dw 2;  number of reading heads
	 BPB_HiddSec dd 0
	 BPB_TotSec32 dd 0

')
For further work with diskette and files, we will write several auxiliary procedures.
The first one is reading one sector at an absolute address, i.e. the sector will be set not by a set of parameters head, cluster, sector, but by one ordinal number.

  1. ;
  2. ; The procedure for reading the sector of the floppy by absolute number
  3. ;
  4. ; Entrance:
  5. ; dx - absolute sector number
  6. ; si - buffer address
  7. ;
  8. k_read_sector :
  9. ; S = N mod 18 + 1
  10. ; T = N / 18
  11. ; H = T mod 2
  12. ; C = T / 2
  13. pusha
  14. mov ax , dx
  15. mov cx , [ BPB_SecPerTrk ]
  16. mov bx si
  17. xor dx , dx ; Starting from here, we recalculate using the formulas
  18. div cx
  19. mov ch , al
  20. shr ch , 1
  21. mov cl , dl
  22. inc cx
  23. mov dh , al
  24. and dh , 1
  25. mov ax , 0x0201
  26. xor dl , dl
  27. int 0x13
  28. jnc @f ; If the C flag is set, an error has occurred.
  29. mov si , msgErrorRead
  30. call k_puts ; Let us know
  31. @@ :
  32. popa
  33. ret


For those who are not familiar with FASM I will explain about jump tags.
@@ is a universal label, it can occur any time in the code;
@b - jump to the first label @@ up the code (back);
@f - jump to the first label @@ further along the code (forward);


The next procedure is an add-on over this one, and it will read several sectors in a row. You can complain, but where are the checks? I do not check for correctness of sector numbers to save space.

  1. ;
  2. ; Procedure for sequential reading of multiple sectors
  3. ;
  4. ; Entrance:
  5. ; dx - starting sector
  6. ; cx - how many sectors to read in a row
  7. ; si - memory address where to read
  8. ;
  9. k_read_sectors :
  10. push dx
  11. push cx
  12. @@ :
  13. call k_read_sector
  14. inc dx
  15. add si , [ BPB_BytsPerSec ]
  16. dec cx
  17. jnz @b ; We read yet not 0
  18. pop cx
  19. pop dx
  20. ret


As you can see, there is nothing difficult yet, but the functionality is added.
Now actually tell you what we need to read the file. To begin with we consider in memory the entire FAT table and root directory.

FAT table and root directory


The entire table FAT12 consists of records of 12 bits (!), Which are combined into chains. The numbers indicate the absolute address of the sector. We read until the number is 0xFFF - this is the end of the chain.
Those. if the file is 513 bytes, then 2 sectors will be allocated for it, even though the second one will be occupied by one byte only.
Now as for the table of the main directory - it consists of 32-byte records, which contains all the data about the file.
Here is its format:

 +0 11 File name in the format 'IIIIIIIIRRRR'
		 The file name is 8 characters long, if shorter - filled with spaces.  There is no separator point.
		 3byte extension
 + 0Bh 1 File Attributes: 
			 01h - Read Only 
			 02h - Hidden 
			 04h - System 
			 08h - Volume Tag 
			 10h - Directory 
			 20h - Archive 
 + 0Ch 10 Reserved 
 + 16h 2 Time to create or modify in filetime format 
 + 18h 2 Date of creation or modification in filetime format 
 + 1Ah 2 Number of the first entry in the chain of FAT
 + 1Ch 4 Size

One feature - when deleting files, the records themselves are not deleted, but only the first byte of the name is replaced with the character 0xE5.
Files with zero length can not be - because thus folders are designated, and at offset + 1Ah the number of the first entry nested in the file is recorded.
The first two of which are. and .., which respectively indicate the first entry in the current directory, and the parent.

Let's write two more very small procedures - which will read both tables into memory.

  1. ;
  2. ; The procedure reads the FAT table into memory.
  3. ;
  4. k_read_fat :
  5. mov dx , 1 ; It is located right behind the boot sector.
  6. mov cx , [ BPB_FATSz16 ] ; 9 sectors
  7. mov si , FAT
  8. call k_read_sectors
  9. ret
  10. ;
  11. ; The procedure reads the root directory into memory.
  12. ;
  13. k_read_root_dir :
  14. mov dx , 19 ; 1 + 9 * 2
  15. mov cx , 15
  16. mov si , ROOT
  17. call k_read_sectors
  18. ret


Reading file


Now, to read into the memory of the file, it remains to collect all that we have written, i.e. find an entry about it in the root directory, and count the sectors from FAT into memory.
Actually, this operation took the most time and code.

  1. ;
  2. ; The procedure reads a file from a floppy disk into memory.
  3. ;
  4. ; Entrance:
  5. ; di - buffer address
  6. ; si - the file name is strictly in the format NNNNNNNNEEE
  7. ; Output:
  8. ; ax - 0 if the file is not found, 1 - found
  9. ;
  10. k_read_file :
  11. push di
  12. mov di , root
  13. mov cx , 0xE0 ; BPB_RootEntCnt
  14. . next_item :
  15. mov al , byte [ di ]
  16. cmp al , 0xE5 ; Remote file label
  17. je . space_item
  18. cmp al , 0 ; Blank entry
  19. je . space_item
  20. push di
  21. push si
  22. push cx
  23. mov cx , 11 ; 8 + 3
  24. repe cmpsb ; Compare the file name with the desired
  25. cmp cx , 0
  26. pop cx
  27. pop si
  28. pop di
  29. je . read_file ; break
  30. . space_item :
  31. add di , 32 ; record length
  32. loop next_item
  33. xor ax , ax
  34. ; jmp .end_of_file
  35. ret
  36. . read_file :
  37. pop si
  38. mov bp , word [ di + 0x1A ] ; Initial cell number FAT
  39. mov bx , word [ di + 0x1C ] ; File size
  40. . read_next_claster :
  41. pusha
  42. mov dx , bp
  43. sub dx , 3
  44. add dx , 0x22
  45. call k_read_sector
  46. popa
  47. cmp di , 0xFFF
  48. je . end_of_file
  49. mov di , bp
  50. mov ax , bp ; save for parity check
  51. mov bx , bp ; save in case there will be 0xFFF
  52. imul di </ f

Source: https://habr.com/ru/post/107030/


All Articles