Os - What does it take to write an operating system? How Windows is created What the operating system is written in.

Os - What does it take to write an operating system? How Windows is created What the operating system is written in.

20.10.2023

rTYCHEFUFCHHA CHUEI UCHPYI YUIFBFEMEK!

rTEDSHDHEYE CHSHCHRKHULY NPZMY VShchFSH OEULPMSHLP ЪBRKhFBOOSCHNY. oBUBMSHOBS ЪБЗТХЛБ, Assembler, BIOS. UEZPDOS NSCH OBLPOEG RETEYIDYN L VPMEE YOFETEUOPK Y RPOSFOPK YUBUFY - NSCH OBUYOBEN RYUBFSH SDTP. th RYUBFSH NSCH EZP VKhDEN ABOUT THE SESSCHLE CHCHUPLPZP HTPCHOS y.

h OBYUBMSHOSHCHK ЪBZTHYUYL PUFBMPUSH CHOEUFY CHUEZP RBTH DPRPMOEOYK Y PO VHDEF RPMOPUFSHA ZPFPCH ZTHYFSH MAVSHCHE 32-VYFOSHCHE SDTB.

PRTEDEMEOYE PVAENB PRETBFYCHOPK RBNSFY

lPOYUOP, NPTsOP RPDUDUYFBFSH PVIAYN RBNSFY CHTHYUOHA CH SDTE - RETEVYTBFSH BDTEUB PF 0x100000 Y RSHCHFBFSHUS ЪBRYUBFSH FKhDB OBYUEOYE PFMYYUOPE PF OHMS Y 0xFF. eUMY RTY YUFEOYY NSCH RPMKHYUBEN RPMHYUOOPE OBYUEOYE, FP CHUЈ IPTPYP, YOBYUE RBNSFSH LPOYUYMBUSH - ЪBRPNYOBEN BDTEU RPUMEDOEZP HDBYUOPZP YUFEOOYS, LFP Y VHDEF PVIENNPN PRETBFYCHOPK RBNSFY. pDOBLP FBLPC URPUPV YNEEF DCHB OEDPUFBFLB:

1) EZP UMEDHEF YURPMSHЪPCHBFSH DP CHLMAYUEOYS UFTBOYUOPK BDTEUBGYY, YuFPVSH YNEFSH DPUFHR LP CHUEK ZHYYUEULPK RBNSFY, MYVP KHUFTBICHBFSH ЪBRYUSH YUETE "PLOP CHTENEO" OPK UFTBOIGSHCH. mYYOSS FTBFB CHTENEY, RTY HUMPCHYY, YuFP FEUFYTPCHBOYE RBNSFY BIOS Y FBL CHSHRPMOSEF RTY OBYUBMSHOPK YOYGYBMYBGYY, B NSCH DEMBEN DCHPKOHA TBVPFH.

2). l FPNH CE BIOS RYYEF CH UBNHA PVSHYUOKHA RBNSFSH FBVMYGSH ACPI, LPFPTSHCHE RTYZPDSFUS PRETBGYPOOPK UYUFEN Y OE UFPYF YI ЪBFYTBFSH DP RTPYUFEOOS.

yЪ LFPPZP UMEDHEF, YuFP MHYUYE URTPUYFSH RTP PVYAEN PRETBFYCHOPK RBNSFY X BIOS, VMBZP ON RTEDPUFBCHMSEF CHUE OEPVIPDYNSCHE ZHKHOLGYY.

yUFPTYYUEULY RETCHPK JHOLGYEK PRTEDEMEOYS PVYANB PRETBFYCHOPK RBNSFY VSHMP RTETSCHCHBOIE 0x12. pOP OE RTOYNBEF OILBLYI CHIPDOSCHI RBTBNEFTPCH, CH ABOUT CHSHCHIPDE CH TEZYUFTE AX UPDETSYFUS TBNET VBPCHPK RBNSFY CH LYMPVBKFBI. vББПЧБС RBNSFSH - FE UBNSHCHE 640 lv DPUFHROSCHE CH TEBMSHOPN TETSINE. UEKYUBU CHSC HCE OE UNPTSEFE OBKFY LPNRSHAFET, ZDE VSH VSHMP NEOEE 640 lv RBNSFY, OP NBMP MY. yURPMSHЪPCHBFSH EЈ OBN UNSHUMB OEF - EUMY RTPGEUUPT RPDDETSYCHBEF ЪBEEYEЈOOSHK TETSYN, FP CHTSD MY KH OEZP VKhDEF NEOSHYE OEULPMSHLYI NEZBVBKF RBNSFY.

pVYaЈNSCH RBNSFY TPUMY Y 640 lv UFBMP NBMP. fPZDB RPSCHYMBUSH OPCHBS ZHKHOLGYS - RTETSCHCHBOYE 0x15 AH=0x88. pOB CHP'CHTBEBEF CH AX TBNET TBUYYTEOOPK RBNSFY (STUDENTS 1 nv) CH LYMPVBKFBI CH AX. bFB ZHOLGYS OE NPTSEF CHPCHTBEBFSH OBYUEOYS VPMSHYE 15 Nov (15 + 1 YFPZP 16 Nov).

lPZDB Y 16 nv UVBMP OEDPUFBFPYuOP RPSCHYMBUSH OPCHBS ZHOLGYS - RTETSCHCHBOIE 0x15, AX=0xE801. POB CHPCHTBEBEF TEKHMSHFBFSCH BC CH 4 TEZYUFTBI:

AX - TBNET TBUYYTEOOOPK RBNSFY DP 16 nv Ch LYMPVBKFBI
BX - TBNET TBUYYTEOOOPK RBNSFY UCHETI 16 nv L VMPLBI RP 64 lv
CX - TBNET ULPOZHYZHTYTPCHBOOPC TBUYYTEOOPK RBNSFY DP 16 nv Ch LYMPVBKFBI
DX - TBNET ULPOZHYZHTYTPCHBOOPC TBUYYTEOOPK RBNSFY UCHETI 16 nv h VMPLBI RP 64 lv

YuFP FBLPE "ULPOZHYZHTYTPCHBOOBS" RBNSFSH RTPYCHPDYFEMY BIOS UHDS RP CHUENKH OE DPZPCHPTYMYUSH, RPFPNKH OBDP RTPUFP, EUMY CH AX Y BX OHMY, VTBFSH OBYUE OYE CX Y DX.

oP Y LFPZP PLBBBMPUSH NBMP. CHEDSH CHUE RETEYUUMEOOSCH CHE ZHKHOLGYY YNEAF PZTBOYUEOOYE PVAENB RBNSFY CH 4 stars, L FPNH TSE OE KHYYFSHCHBAF FP, YuFP RBNSFSH NPTsEF VSCFS OE OERTETCHCHOSCHN VMPLPN. rPFPNH CH OPCHSHHI BIOS RPSCHYMBUSH EEЈ PDOB ZHOLGYS - RTETSCHCHBOIE 0x15, AX=0xE820. POB CHPCHTBEBEF OE RTPUFP YUYUMP, B LBTFH RBNSFY. CHIPDOSHHE RBTBNEFTSCH:

EAX=0xE820
EDX=0x534D4150 ("SMAP")
EBX - UNEEEEOYE PF OYUBMB LBTFSH RBNSFY (DMS OYUBMB 0)
ECX - TBNET VKHZHETB (LBL RTBCHYMP 24 VBKFB - TBNET PDOPZP BMENEOFB)
ES:DI - BDTEU VKHZHETB, LKhDB OBDP ЪBRYUBFSH PYUETEDOPK BMENEOF

CHPIPDOSHE RBTBNEFTSCH:

EAX=0x534D4150 ("SMAP")
EBX - OPChPE UNEEOOYE DMS UMEDHAEEZP CHSHCHJPCHB ZHKHOLGYY. eUMY 0, FP CHUS LBTFB RBNSFY RTPYUIFBOB
ECX - LPMYUEUFChP TEBMSHOP CHPCHTBEYOOOSCHI VBKF (20 YMY 24 VBKFB)
h KHLBBOOPN VKHZHETE UPDETSYFUS PYUETEDOPK BMENEOF LBTFSH RBNSFY.

LBTSDSCHK BMENEOF LBTFSH RBNSFY YNEEF UMEDHAEHA UFTHLFHTH (OBRYYKH CH UYOFBLUIUE UI, RPFPNKH YuFP TBVPT DBOOSHI NSCH VKHDEN DEMBFSH HTSE CH SDTE):

Struct ( unsigned long long base; //vБПЧШЧК ЖЪЪУУУУЛК БДТУ ТЭЗІПOB unsigned long long length; //тБЪNET TEZYPOB CH VBKFBI unsigned long type; // fYR TEZYPOB unsigned long acpi_attrs; //tBUYYTEOOSH BFTYVHFSCH ACPI );

rPUMEDOYK BMENEOF UFTKHLFKhTSCH OE PVSBFEMEO. eeЈ CH PDOPN YUFPYUOYLE CHYDEM, UFP RETED ЪBRTPUPN BMENEOFB UFPYF RPNEUFYFSH FHDB EDYOYULH. lPOYUOP, UEKYUBU NSCHOE RPDDETSYCHBEN ACPI, OP MHYUYE ЪBTBOEE RPBBVPFYFUS P FPN, YUFPVSH RPMKHYUYFSH LBL NPTsOP VPMSHYE DBOOSCHI. h PFMYYUYY PF RBTBNEFTPCH RBNSFY, CHU PUFBMSHOP NPTsOP MEZLP KHOBFSH Y YY ЪBEEYEЈOOOPZP TETSINB OBRTSNHA, VE BIOS.

TEZYPOSH RBNSFY, PRYUSCHCHBENSHCH LBTFPC, NPZHF VShchFSH OUEULPMSHLYI FYRPCH:

1 - pVSHYUOBS RBNSFSH. nPTsEF VShchFSH UCHPVPDOP YURPMSHЪPCHBOB pu DMS UCHPYI GEMEK. rPLB NSCH FPMSHLP L OEK Y VKhDEN PVTBEBFSHUS, B CHUY PUFBMSHOP RTPRKHULBFSH.
2 - ъБТЭЧТПЧБОП (OBRTYNET, LPD BIOS). bFB RBNSFSH NPTSEF VSCHFSH LBL ZHJYYUEULY OEDPUFHROB DMS ЪBRYUY, FBL Y RTPUFP ЪBRYUSH FHDB OETSEMBFEMSHOB. fBLHA RBNSFSH MHYUYE OE FTPZBFSH.
3 - dPUFHROP RPUME RTPYUFEOOIS FBVMYG ACPI. CHETPSFOP, YNEOOP CH FYI VMPLBI LFY FBVMYGSH Y ITBOSFUS. rPLB DTBKCHET ACPI OE RTPYUIFBEF FBVMYGSHCH, bFH RBNSFSH MHYUYE OE FTPZBFSH. rPFPN NPTsOP YURPMSHЪPCHBFSH FBL CE, LBL Y RBNSFSH FYRB 1.
4 - bFH RBNSFSH UMEDHEF UPITBOSFSH NETSDH NVS UEUUYSNY. fBLHA RBNSFSH NSCH FTPZBFSH OE VKhDEN, RPLB OE KHOBEN, YuFP FBLPE NVS UEUUYY:-)

OE CHUE BIOS NPZHF RPDDETSYCHBFSH UFH ZHKHOLGYA. eUMY LBLBS-FP ZHOLGYS OE RPDDETTSYCHBEFUS, FP RTY CHSHCHIPDE YЪ OEЈ KHUFBOPCHMEO ZHMBZ RETERPMOOYS Y UMEDHEF PVTBEBFSHUS L VPMEE UFBTPC. nsch VKhDEN YURPMSHЪPCHBFSH ZHTNBF LBTFSH RBNSFY ZHKHOLGYY 0xE820. eUMY UBNH YFKH ZHOLGYA CHSHCHBFSH OE RPMKHYUMPUSH - RPMKHYUBFSH PVIAYN RBNSFY PVSHCHYUOSCHNY UTEDUFCHBNY Y UPJDBCHBFSH UCPA UPVUFCHEOKHA LBTFH RBNSFY Y PDOPZP FB. rPULPMSHLH PRTEDEMEOYE PVYaЈNB RBNSFY ЪBDББУБ ОХЦОБС ЪДС ЪБРХУЛБ 32-VYFOPZP ЪДС ЪБРХУЛБ 64-VYFOPZP SDTB, MHYUYE PZhPTNYFSH EЈ CH CHYDE RPDRTPZTBNNNSCH. lBTFKH RBNSFY TBNEUFYN RP BDTEUKH 0x7000. OE DKHNBA, YuFP POB NPTsEF VShchFSH VPMSHYE RBTSH LYMPVBKF. rPUMEDOYK BMENEOF CHTHYUOKHA UDEMBEN FYRB 0 - FBLPZP FYRB OE CHPTBEBEF BIOS Y LFP Y VHDEF RTYOBLPN LPOGB.

; rPMHYUEOYE LBTFSH RBNSFY get_memory_map: mov di, 0x7000 xor ebx, ebx @: mov eax, 0xE820 mov edx, 0x534D4150 mov ecx, 24 mov dword, 1 int 0x15 jc @f add di, 24 test ebx, ebx jnz @b @ : cmp di, 0x7000 ja .ok mov dword, 0x100000 mov dword, 0 mov dword, 0 mov dword, 1 mov dword, 0 mov ax, 0xE801 int 0x15 jnc @f mov ah, 0x88 int 0x15 jc .ok mov cx, ax xor dx , dx @: test cx, cx jz @f mov ax, cx mov bx, dx @: movzx eax, ax movzx ebx, bx mov ecx, 1024 mul ecx push eax mov eax, ebx mov ecx, 65536 mul ecx pop edx add eax, edx mov , eax add di, 24 jmp .ok .ok: xor ax, ax mov cx, 24 / 2 rep stosw ret

OH CHPF Y ZPFPCH OBY OBYUBMSHOSHCHK ЪБЗТХЪУйЛ VHI 32-VYFOSHCHI SDEt. h ЪBLMAYUEOYE RTYCHPTSKH EZP RPMOSHCHK LPD Y NSCH RETEKDEN L SDTH.

; oYUBMSHOSHCHK ЪБЗТХЪУYЛ SDTB DMS BTIIFELFHTSCH x86 format Binary as "bin" org 0x7C00 jmp boot ; ъБЗПМПЧПЛ ListFS align 4 fs_magic dd ? fs_version dd ? fs_flags dd ? fs_base dq ? fs_size dq ? fs_map_base dq ? fs_map_size dq ? fs_first_file dq ? fs_uid dq ? fs_block_size dd ? ; ъБЗПМПЧПЛ ЖБКМБ virtual at 0x800 f_info: f_name rb 256 f_next dq ? ..",0 ok_msg db "OK",13,10,0 config_file_name db "boot.cfg",0 start16_msg db "Starting 16 bit kernel...",13,10,0 start32_msg db "Starting 32 bit kernel. ..",13,10,0 label module_list at 0x6000 label memory_map at 0x7000 ; @b @: mov byte, 0 mov ax, si pop si ret ; push si mov si, load_msg_suffix call write_str pop si push si bp mov dx, word mov ax, word @: push ax call split_file_name mov bp, ax pop ax call find_file test byte, 1 jz @f mov si, bp mov dx, word mov ax, word jmp @b @: call load_file_data mov si, ok_msg call write_str pop bp si ret ; dword, 1 int 0x15 jc @f add di, 24 test ebx, ebx jnz @b @: cmp di, 0x7000 ja .ok mov dword, 0x100000 mov dword, 0 mov dword, 0 mov dword, 1 mov dword, 0 mov ax , 0xE801 int 0x15 jnc @f mov ah, 0x88 int 0x15 jc .ok mov cx, ax xor dx, dx @: test cx, cx jz @f mov ax, cx mov bx, dx @: movzx eax, ax movzx ebx, bx mov ecx, 1024 mul ecx push eax mov eax, ebx mov ecx, 65536 mul ecx pop edx add eax, edx mov , eax add di, 24 jmp .ok .ok: xor ax, ax mov cx, 24 / 2 rep stosw ret ; rTPDPMTSEOYE OBUBMSHOPZP ЪБЗТХЪУЛБ boot2: ; ъБЗТХЪН ЛПОжІЗХТБГИПООСЧК ЖБКМ ЪБЗТХЪУЛБ mov si, config_file_name mov bx, 0x1000 / 16 call load_file ; CHSPMOYN ЪБЗТХПУОШК ULTYRF mov bx, 0x9000 / 16 mov bp, module_list mov dx, 0x1000 .parse_line: mov si, dx .parse_char: lodsb test al, al jz .config_end cmp al, 10 je .run_command cmp al, 13 je .run_command jmp .parse_char .run_command: mov byte, 0 xchg dx, si cmp byte, 0 je .parse_line ; rHUFBS UFTPLB cmp byte, "#" je .parse_line ; lPNNEOFBTYK cmp byte, "L" je .load_file ; ъБЗТХЛБ ЖБКМБ cmp byte, "S" je .start ; ъBRHUL SDTB; oEYCHEUFOBS LPNBODB mov al, mov [.cmd], al call error db "Unknown boot script command "" .cmd db ? db ""!",13,10,0 .config_end: ; rTY RTBCHYMSHOPN LPOZHYZHTBGYPOOPN ZHBKME NSCH OE DPMTSOSCH UADB RPRBUFSH; jmp reboot; ъБЗТХЛБ ЖБКМБ.load_file: push dx inc si call load_file push ax mov cx, 512 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 mov ax, bx mov cx, 16 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 pop ax shr ax, 9 - 4 add bx, ax add bp, 16 pop dx jmp .parse_line ; ъBRHUL SDTB.start: ; rTPCHETYN, YuFP ЪBZTHTSEO IPFS VSC PDIO ZhBKM cmp bx, 0x9000 / 16 ja @f call error db "NO KERNEL LOADED",13,10,0 @: ; ъBRPMOSEN RPUMEDOYK BMENEOF URYULB ZHBKMPCH xor ax, ax mov cx, 16 mov di, bp rep stosw ; RETEIPDYN L RTPGEDHTE YOYGYBMYBGYY SDTB DMS OKHTSOPK TBTSDOPUFY inc si cmp word, "16" je .start16 cmp word, "32" je .start32 ;cmp word, "64" ;je .start64 ; oEYCHEUFOBS TSTSDOPUFSH SDTB call error db "Invalid start command argument",13,10,0 ; ъBRHUL 16-TBTSDOPZP SDTB.start16: mov si, start16_msg mov bx, module_list mov dl, jmp 0x9000 ; ъBRHUL 32-TBTSDOPZP SDTB.start32: ; CHCHCHPDYN HCHEDPNMEOYE P ЪBRHULE 32-VYFOPZP SDTB mov si, start32_msg call write_str ; rTPCHETYN, YuFP RTPGEUUPT OE IHTSE i386 mov ax, 0x7202 push ax popf pushf pop bx cmp ax, bx je @f call error db "Required i386 or better",13,10,0 @: ; rPMKHYUN LBTFH RBNSFY call get_memory_map ; pYYUFYN FBVMYGSH UFTBOYG xor ax, ax mov cx, 3 * 4096 / 2 mov di, 0x1000 rep stosw ; ъBRPMOIN LBFBMPZ UFTBOIG mov word, 0x2000 + 111b mov word, 0x3000 + 111b ; ъBRPMOIN RETCHHA FBVMYGH UFTBOYG mov eax, 11b mov cx, 0x100000 / 4096 mov di, 0x2000 @: stosd add eax, 0x1000 loop @b ; ъBRPMOYN RPUMEDOAA FBVMYGH UFTBOYG mov di, 0x3000 mov eax, dword or eax, 11b mov ecx, dword shr ecx, 12 @: stosd add eax, 0x1000 loop @b mov word, 0x4000 + 11b ; Kernel stack mov word, 0x3000 + 11b ; Kernel page table ; ъБЗТХЪН ЪБУЕОЕЧ CR3 mov eax, 0x1000 mov cr3, eax ; ъБЗТХХЪН ЪБУУЭОЭ Ч GDTR lgdt ; ъBRTEFYN RTETSCHCHBOYS cli; RETEKDEN CH ЪBEEYEЈOOOSCHK TETSYN mov eax, cr0 or eax, 0x80000001 mov cr0, eax ; RETEKDEN ABOUT 32-VYFOSHCHK LPD jmp 8:start32; fBVMYGB DEULTYRFPTPCH UEZNEOFPPCH DMS 32-VYFOPZP SDTB align 16 gdt32: dq 0 ; NULL - 0 dq 0x00CF9A000000FFFF ; CODE - 8 dq 0x00CF92000000FFFF ; DATA - 16 gdtr32: dw $ - gdt32 - 1 dd gdt32 ; 32-VYFOSHCHK LPD use32 start32: ; oBUFTPYN UEZNEOFOSH TEZYUFTSHY UFEL mov eax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0xFFFFDFFC ; rPNEUFYN CH DL OPNET ЪБЗТХЪПУОПЗП ДУЛБ mov dl, ; rPNEUFYN CH EBX BDTEU URYULB ЪБЗТХЦеOOШ itsХБКМПЧ mov ebx, module_list ; rPNEUFYN CH ESI BDTEU LBTFSH RBNSFY mov esi, memory_map ; RETEIPDYN ABOUT SDTP jmp 0xFFC00000

RETCHPE SDTP

sDTP RPLB KH OBU VKhDEF UPUFPSFSH YJ DCHHI ZHBKMPCH - startup.asm Y main.c. startup.asm OHTSEO DMS FPZP, YuFPVSH VShchFSH KHCHETEOOSCHNY, YuFP KHRTBCHMEOYE RPRBDEF ABOUT ZHKHOLGYA kernel_main. CHEDSH POB NPTsEF VSHFSH OE CH OBYUBME ZHBKMB, B UPDETSINPE startup.o NSCH RPMOPUFSHA LPOFTPMYTHEN Y EUMY KHLBTSE EZP RETCHSHCHN MYOLETKH, FP VHDEN HRTBCHMSFSH Y RETCHSHNY VBKFBNY DCHPYUOPZ ZHBKMB .

Format ELF public _start extrn kernel_main section ".text" executable _start: movzx edx, dl push edx push esi push ebx lgdt call kernel_main @: ;cli ;hlt jmp @b section ".data" writable gdt: dq 0 dq 0x00CF9A000000FFFF dq 0x00CF92000000 FFFF gdtr: dw $ - gdt dd gdt

OH CHPF Y RPUMEDOYK ABOUT LPD ABOUT YUYUFPN Assembler :-). BY CHSHCHRPMOSEF RTPUFEKYHA ЪBDББУХ - ХМПЦИФШ Ш УФЭЛ ФТИ БТЗХНЭОФБ DMS ZHKHOLGYY kernel_main Y RETEDBFSH ABOUT OEE HRTBCHMEOYE. rPUME CHPCHTBFB YЪ OEЈ SDTP KHIPDYF CH VEULPOYUOSCHK GYLM. rP UPZMBYEOYA CHSHCHJPCHB ZHKHOLGYK ui RBTBNEFTSCH UMEDHEF RYIBFSH CH UFEL CH PVTBBPN RPTSDLE. fBLCE LFPF LPD YOYGYBMYBGYY ЪBZTHTSBEF OPCHPE OBYUEOYE CH GDTR - FERTSH FBVMYGB DEULTYRFPTPCH UEZNEOFPCH OBIPDIFUS CH RTPUFTBOUFCHE SDTB Y DBCE EUMY NSCH PFNPO FYTHEN RETCHSCHK NEZBVBKF OE RTPYJPKDEF OILBLYI PYYVPL.

b FERTSH UBNPE CHLHUOPE - RTPUFEKEEE SDTP ABOUT THE SETTING CHCHUPLPZP HTPCHOS:

Typedef struct ( unsigned long long base; unsigned long long size; ) BootModuleInfo; void kernel_main(char boot_disk_id, void *memory_map, BootModuleInfo *boot_module_list) ( char *screen_buffer = (void*)0xB8000; char *msg = "Hello world!"; unsigned int i = 24 * 80; while (*msg) ( screen_buffer = *msg; i++;

bFP SDTP OE DEMBEF OYUEZP PUPVEOOOPZP - RTPUFP CHCHCHPDYF UFTPLH "Hello world!" ABOUT RPUMEDOAA UFTPYULH FELUFPCHPZP LTBOB. uFTHHLFKHTB PRYUBOOBS CH OBYUBME VKhDEF OHTSOB DMS DPUFKHRB L URYULKH ЪBZTHTSEOOSCHI NPDHMEK.

chBTsOP RPNOYFSH, YuFP OILBLPK UFBODBTFOPK VYVMYPFELY KH OBU OEF - OBN DPUFHROSCH FPMSHLP FE ZHKHOLGYY, LPFPTSHCHE NSCH UDEMBEN UBNY. CHUE printf, strcpy, memcpy Y F. R. RTYDFUS TEBMYЪPCHCHCHBFSH UBNPUFPSFEMSHOP, OE RSCHFBKFEUSH PVTBFYFSHUS LOYN. h UMEDHAEEN CHSHCHRKHULE NSCH ЪБКНЈНУС UPЪDBOYEN OBEZP UPVUFCHEOOPZP TsKhFLP KhTEЪBOOPZP BOBMMPZB libc, YUFPVSH RTPZTBNNNYTPCHBFSH VSHMP KhDPVOEE. FHF OBUYOBEFUS UBNBS YOFETEUOBS YUBUFSH, B RTYOSFSHCHE TEYEOYS PE NOPZPN RPCHMYSAF ABOUT CHUA UFTHLFHTH UYUFENSCH.

uVPTLB SDTB

yURPMOSENSCH ZHBKMSCH UPVYTBAFUS CH DCHB LFBRB - LPNRYMSGYS, B RPFPN MYOLPCHLB. ABOUT RETCHPN LFBR LPNRYMSFPT RTEPVTBHEF YUIPDOSHCHK LPD CH LPNBODSCH RTPGEUUPTB Y UPITBOSEF CHUY LFP CH PVYAELFOSHCHK ZHBKM. LBTSDSCHK NPDHMSH UYUFENSCH UPITBOSEFUS CH PFDEMSHOPN ZHBKME. h LFPN ZHBKME FBL CE UPDETSYFUS YOZHPTNBGYS P ZHKHOLGYSI, PRYUBOOSCHI CH NPDHMY, RPFPNH YЪ PDOPZP ZHBKMB NPTsOP UCHPVPDOP CHSCCHCHBFSH ZHKHOLGYA YЪ DTHZPZP. CHEUSH LPD CH PVAELFOSCHI ZHBKMBI OE RTYCHSBO L LPOLTEFOSHCHN BDTEUBN. ABOUT CHFPTPN LFBR MYOLET UPVYTBEF CHUE PVAELFOSCH ZHBKMSCH CH PDYO VYOBTOSHCHK. rTY LFPN LPD RTYCHSCHCHBEFUS L LPOLTEFOSHN BDTEUBN (EUMY, LPOYUOP, NSCH OE UPVYTBEN DIOBNYUEULY ЪBZTHTSBENKHA VYVMYPFELH), CHNEUFP UUSCHMPL O ZHKHOLGYY RPDUFBCHMSAFUS O HTSOSHE BDTEUB. OBN OHTsOP RPMKHYUFSH ABOUT CHSHCHIPDE PUPVSHCHK DCHPYUOSCHK ZhBKM. ьFP RTPUFP LPD Y DBOOSCH, VEЪ LBLYI-MYVP ЪБЗПМЧЛПЧ (ФП EUФШ ЛФП OE PE І OE ELF). h LBUEUFCHE VBPCHPZP BDTEUB YURPMSH'HEFUS BDTEU 0xFFC00000. DMS HRTPEEOYS LFPPZP NSCH PRYYEN CHUЈ, YuFP OBN OHTSOP CH UREGYBMSHOPN ZHTNBFE ULTYRFB ld:

OUTPUT_FORMAT("binary") ENTRY(_start) SECTIONS ( .text 0xFFC00000: ( *(.text) *(.code) *(.rodata*) ) .data ALIGN(0x1000) : ( *(.data) ) .bss ALIGN(0x1000) : ( *(.bss) ) .empty ALIGN(0x1000) - 1: ( BYTE(0) ) )

ьFPF ULTYRF ZPCHPTYF, YuFP OBY ZHBKM VKhDEF METSBFSH CH RBNSFY OERTETCHCHOSCHN VMPLPN OBUYOBS U BDTEUB 0xFFC00000. h UBNPN OBYUBME VKhDEF YDFY UELGYS LPDB, RPFPN UELGYS read-only DBOOSCHI, ЪBFEN PVSHYUOSHI DBOOSCHI, RPFPN OEYOYYBMYYYTPCHBOOSCHI. Chue UELGYY CHSHTPCHOOESCH ABOUT TBNET UFTBOYGSCH 4 levs (CHDTHZ NSCH RPFPN ЪBIPFYN ЪBEIFYFSH ABOUT HTPCHOE FBVMYGSHCH UFTBOYG LPD PF ЪBRYUY). rPUMEDOEE PRYUBOIE UELGYY.empty OEPVIPDYNP DMS FPZP, YUFPVSH DBTSE OEYOYYYBYMYYPTPCHBOOSCH RETENEOOOSCH ЪBOINBMY NEUFP CH ZHBKME (FBN VHDHF OHMY). CHEDSH OBYUBMSHOSHCHK ЪBZTHYUYL CHSHCHDEMSEF RBNSFSH DMS SDTB THLPCHPDUFCHHSUSH TBNETPN ZHBKMB.

uPVTBFSH CHUY SDTP NPTsOP UMEDHAEYNY LPNBODBNY:

Fasm startup.asm startup.o gcc -c -m32 -ffreestanding -o main.o main.c ld --oformat=binary -melf_i386 -T script.ld -o kernel.bin startup.o main.o

rBTBNEFT GCC -ffreestanding KHLBSCCHBEF ENKH PFLMAYUYFSH CHUE UFBODBTFOSCH VYVMYPFELY. CHEDSH SING RTYCHSBOSCH L LPOLTEFOPK PRETBGYPOOPK UYUFENE, B NSCH RYYEN OPCHHA.

uVPTLB PVTBB DYULB

pVPKDHUSH VE MYYOYI LPNNEOFBTYECH Y RTPUFP RTYCHEDH MYOHLUPCHSHCHK ULTYRF UVPTLY PVTBBB:

Dd if=bin/boot.bios.bin of=bin/boot_sector.bin bs=512 count=1 dd if=bin/boot.bios.bin of=disk/boot.bin bs=1 skip=512 cp bin/kernel .bin disk/kernel.bin bin/make_listfs of=disk.img bs=512 size=2880 boot=bin/boot_sector.bin src=./disk

according to RTEDRPMBZBEF, YuFP CHUE ULPNRYMYTPCHBOOSCH ZHBKMSCH METSBF Ch bin Ch FELKHEEN LBFBMPZE, B EEЈ YNEEFUS LBFBMPZ disk, Ch LPFPTPN METSYF boot.cfg UMEDHAEEZP UPDETSBOYS:

# Loading kernel Lkernel.bin # Boot 32 bit kernel S32

eUMY CHU CHUY UDEMBMY RTBCHYMSHOP, RPMKHYUEOOOSCHK PVTB NPTsOP ЪBRKHUFYFSH CH BNHMSFPTE YMY DBTSE ABOUT TEBMSHOPN TSEMEY CHSHCH RPMKHYUFE RPDPVOKHA LBTFYOKH:

ъБЗТХЪУйЛ УУИФШЧЧБЭФ ЛПОСХИЗХТБГИПООСЧК ЖБКМ, ъБЗТХЦБЭФ SDПП, РЭТЭИПДИФ Х ЪБЭИЭЈOOШЧК TETSYN Y RETEDBЈF ENKH KHRTBCHMEOYE. rPMHYUYCH EZP, OBIYE SDTP CHSHCHCHPDYF RPUMEDOAA UFTPLH ABOUT BLTBO. bFP MYYSH OBYUBMP DPMZPZP RKhFY, NSCH RETEIPDYN L UBNPK YOFETEUOPK YUBUFY TBTBVPFLY. FERTSH CHSHCHRKHULY VKHDHF ZPTBJDP VPMEE RTPUFSHCHN DMS CHPURTYSFYS, VMBZPDBTS YURPMSH'PCHBOYA SJSHLB CHSHCHUPLPZP HTPCHOS, LPFPTSCHK LBL S OBDEAUSH CHUE Y FBL ЪOBAF. eUMY CHCH OE IPFYFE TBVYTBFSHUS U Assembler, NPTSEFE RTPUFP CHOSFSH NPK ZPFPCHSHCHK UBZTHYUYL J startup.asm Y YNEOSFSH HCE FPMSHLP UPDETSINPE main.c, RPULPMSHLH CHEUSH LPD DP LFPZP OE DYLFHEF TsЈUFLP LBLYE-MYVP RBTBNEFTSH SDTB (LTPNE zhu U LPFPTPK NSCH ЪBZTHTSBENUS) Y RPЪCHPMSEF RPUFTPIFSH ABOUT UCHPEK VBJE YFP KhZPDOP.

bChFPNBFYBGYS UVPTLY YMY Makefile

ChSCH NPZMY ЪBNEFYFSH, YuFP CHTHYUOKHA OBVYCHBFSH UFPMSHLP LPNBOD DPUFBFPYUOP KhFPNYFEMSHOP. l FPNH CE OE CHUEZDB EUFSH OEPVIPDYNPUFSH RETELPNRYMYTCHBFSH CHUE ZhBKMSCH. OBRTYNET, EUMY startup.asm OE VSHM YЪNEOЈO, NPTsOP OE CHSHCHCHBFSH fasm. UREGYBMSHOP DMS HRTPEEOYS LPNRYMSGY RTYMPTSEOYK VSHMB RTYDKHNBOB HFYMYFB make, LPFPTBS CHIPDIF CH UFBODSBTFOHA RPUFBCHLH GCC Y MinGW.

mAVPK Makefile UFPYF YЪ OBVPTB RTBCHYM U FBLPK UFTHHLFKhTPK:

YNSGEMYYMYZHBKMB: yNSRETCHPZPyUIIPDOPZPzhBKMB yNSchFPTPZPyUIIDDOPZPzhBKMB...lPNBODSCHLPNRYMSGYY

RETCHPE RTBCHYMP, LPFPTPE DPMTSOP VShchFSH CH MAVPN Makefile - GEMS all. make UNPFTYF ABOUT ЪBCHYUYNPUFY GEMY all Y LPNRYMYTHEF YI, B ЪBFEN CHSHPRMOSEF LPNBODSCH Y LFK GEMY. dMS LBTSDPK DTHZPK GEMY UOBYUBMB UPVYTBAFUS EЈ ЪBCHYUYNPUFY. rTY LFPN YNS GEMY Y YNS ЪBCHYUINPUFEK NPZHF UPCHRBDBFSH U YNEOBNY TEBMSHOSCHI ZHBKMPCH. h FBLPN UMKHYUBE RETEUVPTLB GEMY RTPYЪPKDF FPMSHLP EUMY YUIPDOYLY VSHMY YЪNEOOSHCH.

eEЈ PDOB GEMSH, LPFPTBS YBUFP YURPMSHЪHEFUS CH Makefile - clean. eЈ ЪБДББУБ ХДБМІФШ CHUE VYOBTOSH ZHBKMSCH, YUFPVSH OBYUBFSH UVPTLH "U YUYUFPZP MYUFB". chPF FBL NPTSEF CHSHCHZMSDEFSH Makefile VHI SDTB:

All: startup.o main.o script.ld ld --oformat=binary -melf_i386 -T script.ld -o kernel.bin startup.o main.o startup.o: startup.i386.asm fasm startup.i386.asm startup.o main.o: main.c gcc -c -m32 -ffreestanding -o main.o main.c clean: rm -v *.o kernel.bin

bFPF FELUF OEPVIPDYNP UPITBOYFSH CH ZHBKM U YNEOEN Makefile (VEY TBUYYTEOYS) CH LBFBMPZ U YUIPDOSHNY FELUFBNY SDTB. FERTSH DPUFBFPYUOP CHSHPRMOYFSH LPNBODH make VEЪ RBTBNEFTPC, OBIPDSUSH CH LFPN LBFBMPZE Y NSCH RPMKHYUN ZHBKM kernel.bin (MYVP UPPVEEOYS PV PYYVLBY, EUMY YUFP-FP RPYMP OE FBL).

b CHPF FBL S UPVYTBA ЪBZTHYUYL:

All: boot.bios.bin boot.bios.bin: boot.bios.asm fasm boot.bios.asm boot.bios.bin clean: rm -v boot.bios.bin

th make_listfs:

All: compile compile: make_listfs.c gcc -o make_listfs make_listfs.c clean: rm -f make_listfs make_listfs.exe

OH Y OBLPOEG TBUULBTSH RTP CHSHCHJPCH DTHZYI Makefile YЪ PDOPZP. with DPUFBFPYUOP MEOYCH, YuFPVSH DBTSE ЪBIPDYFSH CH LBFBMPZY U LBTSDSCHN LPNRPEOFPN UYUFENSH, RPFPNH UPЪDBM 1 Makefile, LPFPTSCHK UPVYTBEF UTBH CHUA UYUFENKH. x NEOS EUFSH RBRLB src, CH OEK RPDLBFBMPZY: boot, kernel, make_listfs. h UBNPC src OBIPDFUS CHPF FBLPC Makefile:

All: make -C boot/ make -C kernel/ make -C make_listfs/ clean: make -C boot/ clean make -C kernel/ clean make -C make_listfs clean

FERETSH, OBIPDSUSH LBFBMPZE src S RTPUFP RYYKH make Y RPMHYUBA RPMOPUFSHA UPVTBOOHA UYUFENKH, B EUMY OBRYUBFSH make clean, FP CHUE DCHPYYUOSCH ZHBKMSCH VHDHF KHDBMEOSH Y PUFBOKHFUS FPMSHLP Y WHIPDOYLE.

OH Y CH DPCHETYEOYE RPUMEDOYK ULTYRF, LPFPTSCHK CHSHRPMOSEF RPMOHA LPNRYMSGYA Y UVPTLH CHUEI LPNRPEOFPCH Y PVTBB DYULB. h PDOPN LBFBMPZE U OIN OBDP TBBNEUFYFSH src, RHUFPK LBFBMPZ bin Y LBFBMPZ disk U ZHBKMPN boot.cfg.

#!/bin/sh make -C src cp src/boot/boot.bios.bin bin/ cp src/kernel/kernel.bin bin/ cp src/make_listfs/make_listfs bin/ dd if=bin/boot.bios.bin of=bin/boot_sector.bin bs=512 count=1 dd if=bin/boot.bios.bin of=disk/boot.bin bs=1 skip=512 cp bin/kernel.bin disk/kernel.bin bin/make_listfs of=disk.img bs=512 size=2880 boot=bin/boot_sector.bin src=./disk read -p "Press Enter to continue..." dummy

u FBLYN OBVPTPN ULTYRFPCH UVPTLB UYUFENB UFBOPCHYFUS RTEDEMSHOP RTPUFPK, PUPVEOOOP EUMY HYUEUFSH, YuFP RPUMEDOYK ULTYRF NPTsOP ЪBRHULBFSH DCHPKOSCHN LMYLPN YJBKMPCHPZP NEOEDCETB. TBMYUOSHE LPNBODSCH CHTPDE dd, cp, rm OE UHEEUFCHHAF RPD Windows, RPFPNH EЈ RPMSHЪPCHBFEMSN RTYZPDYFUS RBBLEF MSYS YMY Cygwin. pDOBLP RTPUFBS UVPTLB CHUEI LPNRPEOFPCH VHDEF TBVPFBFSH DBCE EUMY X CHBU EUFSH FPMSHLP GCC Y fasm (make_listfs MEZLP ULPNRYMYTHEFUS Y ЪBRHUFYFUS CHYDE Windows-RTYMPTSEOYS ).

rTYNEYUBOYE DMS RPMSHЪPCHBFEMEK pu Windows

ld DMS Windows OE UPCHUEN RPMOPGEOOSHCHK - PO OE RPDDETSYCHBEF CHCHCHPD UTBH CH VYOBTOSHCHK ZHBKM, FPMSHLP CH EXE. YURTBCHYFSH LFP NPTsOP UPЪDBCH UOBYUBMB EXE (ld OE PVTBFYF CHOYNBOYE, YuFP VBPCHSHCHE BDTEUB UELGYK OECHPNPTSOSCHE DMS CHEODPCHSHHI VYOBTOILPC), B RPFPN CHSHCHFBEIFSH PFFHDB YUYUFSHCHE MORE AT RPNPESH objcopy. eUMY CHSH UFPMLOЈFEUSH U FEN, YuFP ld PFLBSCHCHBEFUS UPЪDBCHBFSH ZhBKM kernel.bin, CHPURPMSHKFEUSH CHPF FBLYN Makefile VHI SDTB:

All: startup.o main.o script.ld ld -melf_i386 -T script.ld -o kernel.bin startup.o main.o objcopy kernel.bin -O binary startup.o: startup.i386.asm fasm startup.i386 .asm startup.o main.o: main.c gcc -c -m32 -ffreestanding -o main.o main.c clean: rm -v *.o kernel.bin

ъБПДОП ХВЭТИФЭ УФТПЛХ OUTPUT_FORMAT("binary") Ъ script.ld. FERETSH Y RPD Windows RPMKHYUFUS UPVTBFSH SDTP UYUFENSCH.

ъБЗТХЪЛБ УИУФЭНШЧ ABOUT TEBMSHOPK NBYYOE

rPUME FBLYI KHUREIPCH X OELPFPTSCHI NPTSEF CHP'OILOHFSH TSEMBOE PRTPVPCHBFSH OPCHHA pu ABOUT TEBMSHOPN TSEMEYE. lFP OE RTEDUFBCHMSEF RTPVMEN. u RPNPESHA HxD CH Windows PFLTPKFE DYULEFH YMY ZHMEYLH, CHSHVTBCH CHBTYBOF "pFLTSCHFSH DYUL". rTY PFLTSCHFY ZHMEYLY CHBTsOP PFLTSCHFSH YNEOOOP UBNH ZHMEYLKH, BOE ET TB'DEM. h DTHZPK CHLMBDLE PFLTPKFE disk.img, CHSHCHDEMYFE EZP UPDETSINPE RPMOPUFSHA Y ULPRYTHKFE OB DYUL U EZP UBNPZP OBYUBMB. rPUME LFPPZP NPTsOP OBTSBFSH "uPITBOIFSH" Y DPTsDBFSHUS PLPOYUBOYS ЪBRYUY. CHUE DBOOSCH ABOUT JMY DYULEF RTY LFPN VHDHF HOYUFPTSEOSHCH, B DMS FPZP, YUFPVSH EЈ YURPMSHЪPCHBFSH UOPCHB RP OBYUEOYA, EЈ RTYDFUS ЪBOPChP PFZhPTNBFYTPCHBFSH !

rPMSHЪPCHBFEMY Linux NPZHF RPUFKHRYFSH RTPEE - CHSHRPMOYFSH UREGYBMSHOHA LPNBODH CH FETNYOBME. DMS DYULEFSH:

Dd if=disk.img of=/dev/fd0

DMS ZHMEYLY:

Dd if=disk.img of=/dev/sdX

chNEUFP sdX OBDP RPDUFBCHYFSH OBUFPSEEE YNS KHUFTPKUFCHB (sda, sdb, sdc, sdd Y F. D.). ZMBCHOPE RTY LFPN OE RETERKHFBFSH Y OE ЪBRYUBFSH PVTB ABOUT UYUFENOSHCHK DYUL, HOYUFPTSYCH CHUE DBOOSCH. TBHNEEFUS, PVE LPNBODSCH DPMTSOSCHCHSHCHRPMOSFSHUS PF YNEOY root YMY Y RPNPESH sudo.

rPUME LFPPZP OBDP OBUFTPIFSH CH BIOS ЪБЗТХЛХ У ДУЛЭФШЧ ІМИ ЖМИЛХ (UFBTSHCHE BIOS OE RPDDETSYCHBAF ZHMEYLY) Y OBUMBTSDBFSHUS CHYDPN "Hello world".

ъBLMAYUEOOYE

OH CHPF UPVUFCHOOOP Y CHUЈ ABOUT UEZPDOS. nsch OBLPOEG-FP ЪBLPOYUMY RTPZTBNNNYTPCHBOIE OB Assembler (IPFS Ch u CHU TBCHOP RTYDFUS YOPZDB DEMBFSH BUUENVMETOSCHE CHUFBCHLY DMS TBVPFSH U PVPTKHDPCHBOYEN) Y RETEYMY SB L CHCHUPLPZP HTPCHOS. eEЈ PYUEOSH NOPZP RTEDUFPYF UDEMBFSH. NSC NPTSEFE HCE RTPCHPDYFSH TBMYUOSCH LURETYNEOFSH, JNEOSS NPK main.c, FPMSHLP KHYUFYFE, YuFP MAVBS PYYVLB (DPUFHR L OEURTPEGYTPCHBOOPC RBNSFY, DEMEOYE ABOUT RTICHED) F L RETEЪBZTHЪLE YMY ЪBCHYUBOYA UYUFENSCH (NSCH RPLB OE PVTBVBFSCCHBEN YULMAYUEOYS, RPFPNH RTPGEUUPT OE NPTSEF RTDPDPMTSYFSH TBVPFKH RPUME PYYVLY ). dP CHUFTEYUY!

MAvsche CHPRPTUSCH CHSC NPTSEFE ЪBDBFSH ABOUT NPK BDTEU: [email protected]. y DB, UEKYUBU UBNPE CHTENS DMS TBMYUOSHI YDEK RP LPOGERGYY PU Y RTEDMPTSEOYK.

The book "The Operating System from 0 to 1" is published on GitHub and has over 2,000 stars and 100 forks. As the name implies, after reading it, you will be able to create your own operating system - and, perhaps, few things in the world of programmers could be cooler.

With this book you will learn the following:

  • You will learn how to create an operating system based on hardware technical documentation. This is how it works in the real world, you won't be able to use Google for quick answers.
  • You will understand how computer components interact with each other, from software to hardware.
  • Learn to write code yourself. Blindly copying code is not learning, you will actually learn how to solve problems. By the way, blind copying is also dangerous.
  • Master the familiar tools for low-level development.
  • Get familiar with assembly language.
  • Find out what programs are made of and how the operating system runs them. We gave a short overview of this topic for the curious in .
  • You will figure out how to debug a program directly on hardware with GDB and QEMU.
  • The C programming language. You can quickly master it by following.
  • Basic knowledge of Linux. Just study on our website.
  • Basic knowledge in physics: atoms, electrons, protons, neutrons, voltage.

Guide to creating a kernel for an x86 system. Part 1. Just the core

Let's write a simple kernel that can be booted using the GRUB bootloader on an x86 system. This kernel will display a message on the screen and wait.

How does an x86 system boot?

Before we start writing the kernel, let's understand how the system boots and transfers control to the kernel.

Most processor registers already contain certain values ​​at startup. The register pointing to the address of instructions (Instruction Pointer, EIP) stores the memory address where the instruction executed by the processor lies. The default EIP is 0xFFFFFFFF0. Thus, x86 processors at the hardware level start working at address 0xFFFFFFF0. This is actually the last 16 bytes of the 32-bit address space. This address is called the reset vector.

Now the chipset memory map ensures that 0xFFFFFFF0 belongs to a specific part of the BIOS, not RAM. At this time, the BIOS copies itself to RAM for faster access. Address 0xFFFFFFF0 will only contain an instruction to jump to the address in memory where a copy of the BIOS is stored.

This is how the BIOS code begins to execute. The BIOS first looks for a device that can boot from, in a preset order. A magic number is sought to determine whether the device is bootable (the 511th and 512th bytes of the first sector must be equal to 0xAA55).

When the BIOS finds boot device, it copies the contents of the first sector of the device into RAM, starting from the physical address 0x7c00; then goes to the address and executes the downloaded code. This code is called bootloader.

The bootloader loads the kernel at a physical address 0x100000. This address is used as the starting address in all large kernels on x86 systems.

All x86 processors start out in a simple 16-bit mode called real mode. GRUB bootloader switches mode to 32-bit protected mode, setting the low bit of register CR0 to 1 . Thus, the kernel is loaded in 32-bit protected mode.

Note that in the case of Linux kernel GRUB sees the Linux boot protocols and loads the kernel into real mode. The kernel automatically switches to protected mode.

What do we need?

  • x86 computer;
  • Linux;
  • ld (GNU Linker);

Setting the entry point in assembler

No matter how much you would like to limit yourself to C alone, you will have to write something in assembler. We will write a small file on it that will serve as the starting point for our kernel. All it will do is call an external function written in C and stop the program flow.

How can we make sure that this code is the starting point?

We will use a linker script that links object files to create the final executable file. In this script we will explicitly indicate that we want to load data at address 0x100000.

Here is the assembler code:

;;kernel.asm bits 32 ;nasm directive - 32 bit section .text global start extern kmain ;kmain is defined in the c file start: cli ;block interrupts mov esp, stack_space ;set stack pointer call kmain hlt ;halt the CPU section .bss resb 8192 ;8KB for stack stack_space:

The first instruction, bits 32, is not an x86 assembly instruction. This is a directive to the NASM assembler that specifies code generation for a processor operating in 32-bit mode. In our case this is not necessary, but generally useful.

The section with the code begins on the second line.

global is another NASM directive that makes source code symbols global. This way the linker knows where the start symbol is - our entry point.

kmain is a function that will be defined in the kernel.c file. extern means that the function is declared somewhere else.

Then comes the start function, which calls the kmain function and stops the processor with the hlt instruction. This is why we disable interrupts in advance using the cli instruction.

Ideally, we need to allocate some memory and point to it with a stack pointer (esp). However, it looks like GRUB has already done this for us. However, you will still allocate some space in the BSS section and move the stack pointer to its beginning. We use the resb instruction, which reserves the specified number of bytes. Immediately before calling kmain, the stack pointer (esp) is set to the correct location with the mov instruction.

Kernel in C

In kernel.asm we made a call to the kmain() function. Thus, our “C” code should start execution with kmain() :

/* * kernel.c */ void kmain(void) ( const char *str = "my first kernel"; char *vidptr = (char*)0xb8000; //video mem begins here. unsigned int i = 0; unsigned int j = 0; /* this loops clears the screen * there are 25 lines each of 80 columns; each element takes 2 bytes */ while(j< 80 * 25 * 2) { /* blank character */ vidptr[j] = " "; /* attribute-byte - light grey on black screen */ vidptr = 0x07; j = j + 2; } j = 0; /* this loop writes the string to video memory */ while(str[j] != "\0") { /* the character"s ascii */ vidptr[i] = str[j]; /* attribute-byte: give character black bg and light grey fg */ vidptr = 0x07; ++j; i = i + 2; } return; }

All our kernel will do is clear the screen and display the line “my first kernel”.

First we create a vidptr pointer that points to the address 0xb8000. In protected mode, “video memory” begins from this address. To display text on the screen, we reserve 25 lines of 80 ASCII characters, starting at 0xb8000.

Each character is displayed not by the usual 8 bits, but by 16. The first byte stores the character itself, and the second - attribute-byte . It describes the formatting of the character, such as its color.

To display the green character s on a black background, we write this character in the first byte and the value 0x02 in the second. 0 means black background, 2 means green text color.

Here is the color chart:

0 - Black, 1 - Blue, 2 - Green, 3 - Cyan, 4 - Red, 5 - Magenta, 6 - Brown, 7 - Light Grey, 8 - Dark Grey, 9 - Light Blue, 10/a - Light Green, 11/b - Light Cyan, 12/c - Light Red, 13/d - Light Magenta, 14/e - Light Brown, 15/f - White.

In our kernel we will use light gray text on a black background, so our attribute byte will have the value 0x07.

In the first loop, the program prints a blank symbol over the entire 80x25 zone. This will clear the screen. In the next cycle, characters from the null-terminated string “my first kernel” with an attribute byte equal to 0x07 are written to “video memory”. This will print the string to the screen.

Connecting part

We need to assemble kernel.asm into an object file using NASM; then use GCC to compile kernel.c into another object file. They then need to be attached to the executable boot kernel.

To do this, we will use a binding script that is passed to ld as an argument.

/* * link.ld */ OUTPUT_FORMAT(elf32-i386) ENTRY(start) SECTIONS ( . = 0x100000; .text: ( *(.text) ) .data: ( *(.data) ) .bss: ( *( .bss) ) )

First we will ask output format as 32-bit Executable and Linkable Format (ELF). ELF is a standard binary file format for Unix x86 systems. ENTRY takes one argument specifying the name of the symbol that is the entry point. SECTIONS- this is the most important part. It defines the markup of our executable file. We determine how the different sections should be connected and where to place them.

In parentheses after SECTIONS, the dot (.) displays the position counter, which defaults to 0x0. It can be changed, which is what we are doing.

Let's look at the following line: .text: ( *(.text) ) . The asterisk (*) is a special character that matches any file name. The expression *(.text) means all .text sections from all input files.

Thus, the linker joins all the code sections of the object files into one section of the executable file at the address in the position counter (0x100000). After this, the counter value will be equal to 0x100000 + the size of the resulting section.

The same thing happens with other sections.

Grub and Multiboot

Now all the files are ready to create the kernel. But there is one more step left.

There is a standard for loading x86 cores using a bootloader called Multiboot specification. GRUB will only boot our kernel if it meets these specifications.

Following them, the kernel should contain a header in its first 8 kilobytes. Additionally, this header must contain 3 fields, which are 4 bytes:

  • magical field: contains magic number 0x1BADB002 to identify the core.
  • field flags: we don’t need it, let’s set it to zero.
  • field checksum: if you add it with the previous two, you should get zero.

Our kernel.asm will look like this:

;;kernel.asm ;nasm directive - 32 bit bits 32 section .text ;multiboot spec align 4 dd 0x1BADB002 ;magic dd 0x00 ;flags dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero global start extern kmain ;kmain is defined in the c file start: cli ;block interrupts mov esp, stack_space ;set stack pointer call kmain hlt ;halt the CPU section .bss resb 8192 ;8KB for stack stack_space:

Building the core

Now we will create object files from kernel.asm and kernel.c and link them using our script.

Nasm -f elf32 kernel.asm -o kasm.o

This line will run the assembler to create the kasm.o object file in ELF-32 format.

Gcc -m32 -c kernel.c -o kc.o

The “-c” option ensures that no hidden linking occurs after compilation.

Ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o

This will run the linker with our script and create an executable called kernel.

Setting up grub and starting the kernel

GRUB requires the kernel name to satisfy the pattern kernel- . So rename the kernel. I named mine kernel-701.

Now put it in the directory /boot. To do this you will need superuser rights.

In the GRUB configuration file grub.cfg, add the following:

Title myKernel root (hd0,0) kernel /boot/kernel-701 ro

Don't forget to remove the hiddenmenu directive if present.

Restart your computer and you will see a list of kernels including yours. Select it and you will see:

This is your core! Let's add an input/output system.

P.S.

  • For any kernel tricks, it is better to use a virtual machine.
  • To run the kernel in grub2 the config should look like this: menuentry "kernel 7001" ( set root="hd0,msdos1" multiboot /boot/kernel-7001 ro )
  • if you want to use the qemu emulator, use: qemu-system-i386 -kernel kernel

Original: AsmSchool: Make an operating system
Author: Mike Saunders
Published date: April 15, 2016
Translation: A. Panin
Translation date: April 16, 2016

Part 4: With the skills you've gained from reading the previous articles in this series, you can start developing your own operating system!

What is it for?

  • To understand how compilers work.
  • To understand the instructions central processor.
  • To optimize your code for performance.

Over the course of several months, we went through a complex path that began with the development of simple programs in assembly language for Linux and ended in the last article in the series with the development of self-contained code that runs on a personal computer without an operating system. Well, now we will try to collect all the information together and create a real operating system. Yes, we will follow in the footsteps of Linus Torvalds, but first we need to answer the following questions: “What is an operating system? Which of its functions will we have to recreate?”

In this article, we will focus only on the basic functions of the operating system: loading and executing programs. Complex operating systems perform many more functions, such as managing virtual memory and processing network packets, but they require years of continuous work to implement correctly, so in this article we will consider only the basic functions present in any operating system. Last month we developed a small program that fit into the 512-byte sector of a floppy disk (its first sector), and now we will modify it a little to add the function of loading additional data from the disk.

Boot Loader Development

We could try to reduce the size of our operating system binary code as much as possible to fit it into the first 512-byte sector of the floppy disk, the one that is loaded by the BIOS, but in this case we would not be able to implement any interesting functions. Therefore, we will use these 512 bytes to house the binary code of a simple system boot loader, which will load the OS kernel binary code into RAM and execute it. (After this, we will develop the OS kernel itself, which will load the binary code of other programs from disk and also execute it, but we will talk about this a little later.)

You can download source examples discussed in the article at the link www.linuxvoice.com/code/lv015/asmschool.zip. And this is the code of our system boot loader from a file called boot.asm:

BITS 16 jmp short start ; Jump to label, skipping disc description nop ; Addition before the disk description %include "bpb.asm" start: mov ax, 07C0h ; Load address mov ds, ax ; Data segment mov ax, 9000h ; Stack preparation mov ss, ax mov sp, 0FFFFh ; The stack grows down! cld ; Setting the direction flag mov si, kern_filename call load_file jmp 2000h:0000h ; Transition to the OS kernel binary code loaded from the file kern_filename db "MYKERNELBIN" %include "disk.asm" times 510-($-$$) db 0 ; Padding the binary code with zeros up to 510 bytes dw 0AA55h ; Boot loader binary end marker buffer: ; Start of buffer for disk contents

In this code, the first CPU instruction is the jmp instruction, which is located after the BITS directive, which tells the NASM assembler that 16-bit mode is being used. As you probably remember from the previous article in the series, the execution of the 512-byte binary code loaded from the BIOS from disk starts from the very beginning, but we have to jump to a label to skip a special set of data. Obviously, last month we simply wrote the code to the beginning of the disk (using the dd utility) and left the rest of the disk space empty.

Now we will have to use a floppy disk with a suitable MS-DOS file system (FAT12), and in order to work correctly with this file system, we need to add a set of special data near the beginning of the sector. This set is called a BIOS Parameter Block (BPB) and contains data such as disk label, number of sectors, and so on. It should not interest us at this stage, since more than one series of articles could be devoted to similar topics, which is why we have placed all the instructions and data associated with it in a separate source code file called bpb.asm.

Based on the above, this directive from our code is extremely important:

%include "bpb.asm"

This is a NASM directive that allows the contents of a specified source file to be included in the current source file during assembly. This way we can make our boot loader code as short and understandable as possible by placing all the details of the implementation of the BIOS parameter block in separate file. The BIOS parameter block must be located three bytes after the start of the sector, and since the jmp instruction takes up only two bytes, we have to use the nop instruction (its name stands for "no operation" - this is an instruction that does nothing but waste CPU cycles ) to fill the remaining byte.

Working with the stack

Next, we will have to use instructions similar to those discussed in the last article to prepare registers and the stack, as well as the cld (stands for "clear direction") instruction, which allows us to set the direction flag for certain instructions, such as the lodsb instruction, which, when executed, will increment the value in the SI register rather than decrement it.

After that, we put the address of the string into the SI register and call our load_file function. But think about it for a minute - we haven't developed this feature yet! Yes, this is true, but its implementation can be found in another source code file we included called disk.asm.

The FAT12 file system, used on floppy disks that are formatted in MS-DOS, is one of the simplest file systems available, but it also requires a considerable amount of code to work with its contents. The load_file subroutine is about 200 lines long and will not be shown in this article, since we are considering the process of developing an operating system, not a driver for a specific file system, therefore, it is not very wise to waste space on the log pages in this way. In general, we included the disk.asm source code file almost before the end of the current source code file and can forget about it. (If you are still interested in the structure of the FAT12 file system, you can read the excellent overview at http://tinyurl.com/fat12spec, and then look at the disk.asm source code file - the code contained in it is well commented .)

In either case, the load_file routine loads the binary code from the file named in the SI register into segment 2000 at offset 0, after which we jump to the beginning of it for execution. And that's all - the operating system kernel is loaded and the system bootloader has completed its task!

You may have noticed that our code uses MYKERNELBIN instead of MYKERNEL.BIN as the operating system kernel file name, which fits well into the 8+3 naming scheme used on floppy disks in DOS. In fact, the FAT12 file system uses an internal representation of file names, and we save space by using a file name that is guaranteed not to require our load_file routine to implement a mechanism to look up the dot character and convert the file name to the internal representation of the file system.

After the line with the directive for connecting the disk.asm source code file, there are two lines designed to pad the binary code of the system boot loader with zeros up to 512 bytes and include the end mark of its binary code (this was discussed in the previous article). Finally, at the very end of the code is the "buffer" label, which is used by the load_file routine. Basically, the load_file routine needs free space in RAM to do some intermediate work while searching for a file on disk, and we have plenty of free space after loading the boot loader, so we place the buffer here.

To assemble the system boot loader, use the following command:

Nasm -f bin -o boot.bin boot.asm

Now we need to create a virtual floppy disk image in MS-DOS format and add our bootloader binary code to its first 512 bytes using the following commands:

Mkdosfs -C floppy.img 1440 dd conv=notrunc if=boot.bin of=floppy.img

At this point, the process of developing a system boot loader can be considered complete! We now have a bootable floppy disk image that allows us to load the operating system kernel binary code from a file called mykernel.bin and execute it. Next, a more interesting part of the work awaits us - the development of the operating system kernel itself.

Operating system kernel

We want our operating system kernel to perform many important tasks: display a greeting message, accept input from the user, determine whether the input is a supported command, and execute programs from disk when the user specifies their names. This is the operating system kernel code from the mykernel.asm file:

Mov ax, 2000h mov ds, ax mov es, ax loop: mov si, prompt call lib_print_string mov si, user_input call lib_input_string cmp byte , 0 je loop cmp word , "ls" je list_files mov ax, si mov cx, 32768 call lib_load_file jc load_fail call 32768 jmp loop load_fail: mov si, load_fail_msg call lib_print_string jmp loop list_files: mov si, file_list call lib_get_file_list call lib_print_string jmp loop prompt db 13, 10, "MyOS > ", 0 load_fail_msg db 13, 10, "Not found! ", 0 user_input times 256 db 0 file_list times 1024 db 0 %include "lib.asm"

Before looking at the code, you should pay attention to the last line with the directive to include the lib.asm source code file, which is also located in the asmschool.zip archive from our website. This is a library of useful routines for working with the screen, keyboard, strings and disks that you can also use - in this case we include this source code file at the very end of the main source code file of the operating system kernel in order to make the latter as compact and beautiful as possible . Refer to the lib.asm Library Routines section for additional information about all available subroutines.

In the first three lines of operating system kernel code, we fill the segment registers with data to point to segment 2000 into which the binary code was loaded. This is important for guaranteed correct operation instructions such as lodsb, which must read data from the current segment and not from any other. After this, we will not perform any additional operations on the segments; our operating system will work with 64 KB of RAM!

Further in the code there is a label corresponding to the beginning of the loop. First of all, we use one of the routines from the lib.asm library, namely lib_print_string, to print the greeting. Bytes 13 and 10 before the greeting line are escape characters. new line, thanks to which the greeting will not be displayed immediately after the output of any program, but always on a new line.

After this, we use another routine from the lib.asm library called lib_input_string, which takes the user's keyboard input and stores it in a buffer pointed to in the SI register. In our case, the buffer is declared near the end of the operating system kernel code as follows:

User_input times 256 db 0

This declaration allows you to create a buffer of 256 characters, filled with zeros - its length should be enough to store commands for a simple operating system like ours!

Next we perform user input validation. If the first byte of the user_input buffer is zero, then the user simply pressed the Enter key without entering any command; Don't forget that all strings end with null characters. So in this case we should just go to the beginning of the loop and print the greeting again. However, in case the user enters any command, we will have to check first to see if he entered the ls command. Until now, you could only observe comparisons of individual bytes in our assembly language programs, but do not forget that it is also possible to compare double-byte values ​​or machine words. In this code, we compare the first machine word from the user_input buffer with the machine word corresponding to the ls line and, if they are identical, move to the code block below. Within this block of code, we use another routine from lib.asm to get a comma-separated list of files on disk (which should be stored in the file_list buffer), print that list to the screen, and move back into the loop to process user input.

Execution of third party programs

If the user doesn't enter the ls command, we assume they entered the name of the program from disk, so it makes sense to try to load it. Our lib.asm library contains an implementation of the useful lib_load_file routine, which parses the FAT12 disk file system tables: it takes a pointer to the beginning of a line with the file name through the AX register, as well as an offset value for loading binary code from a program file through the CX register. We're already using the SI register to store a pointer to the string containing user input, so we copy that pointer to the AX register and then place the value 32768, which is used as an offset to load the binary code from the program file, into the CX register.

But why do we use this particular value as the offset to load binary code from a program file? Well, this is just one of the memory map options for our operating system. Because we are working in a single 64 KB segment and our kernel binary is loaded at offset 0, we have to use the first 32 KB of memory for kernel data and the remaining 32 KB for load program data. Thus, offset 32768 is the middle of our segment and allows us to provide sufficient RAM to both the operating system kernel and loaded programs.

The lib_load_file routine then performs a very important operation: if it cannot find a file with the given name on disk, or for some reason cannot read it from disk, it simply exits and sets a special carry flag. This is a CPU state flag that is set during the execution of some mathematical operations and should not interest us at the moment, but at the same time we can determine the presence of this flag to make quick decisions. If the lib_load_asm routine sets the carry flag, we use the jc (jump if carry) instruction to jump to a block of code that prints the error message and returns to the beginning of the user input loop.

In the same case, if the transfer flag is not set, we can conclude that the lib_load_asm subroutine has successfully loaded the binary code from the program file into RAM at address 32768. All we need in this case is to initiate the execution of the binary code loaded at this address , that is, start execution specified by the user programs! And after the ret instruction is used in this program (to return to the calling code), we will simply need to return to the loop for processing user input. Thus we created an operating system: it consists of the simplest command parsing and program loading mechanisms, implemented in about 40 lines of assembly code, albeit with a lot of help from routines from the lib.asm library.

To assemble the operating system kernel code, use the following command:

Nasm -f bin -o mykernel.bin mykernel.asm

After this we will have to somehow add the mykernel.bin file to the floppy disk image file. If you are familiar with the trick of mounting disk images using loopback devices, you can access the contents of the disk image using floppy.img, but there is an easier way using GNU Mtools (www.gnu.org/software /mtools). This is a set of programs for working with floppy disks that use MS-DOS/FAT12 file systems, available from the repositories of software packages of all popular Linux distributions, so all you have to do is use apt-get, yum, pacman, or whatever utility you use to install software packages on your distribution.

After installing the appropriate software package, you will have to run the following command to add the mykernel.bin file to the floppy.img disk image file:

Mcopy -i floppy.img mykernel.bin::/

Note the funny symbols at the end of the command: colon, colon, and slash. Now we're almost ready to launch our operating system, but what's the point if there are no apps for it? Let's correct this misunderstanding by developing an extremely simple application. Yes, now you will be developing an application for your own operating system - just imagine how much your authority will rise among the ranks of geeks. Save the following code in a file named test.asm:

Org 32768 mov ah, 0Eh mov al, "X" int 10h ret

This code simply uses the BIOS function to print an "X" on the screen, and then returns control to the code that called it - in our case, that code is the operating system code. The org line that begins the application's source code is not a CPU instruction, but a NASM assembler directive telling it that the binary code will be loaded into RAM at offset 32768, and therefore all offsets must be recalculated to account for this.

This code also needs to be assembled, and the resulting binary file needs to be added to the floppy disk image file:

Nasm -f bin -o test.bin test.asm mcopy -i floppy.img test.bin::/

Now take a deep breath, get ready to contemplate the unparalleled results of your own work, and boot the floppy disk image using a PC emulator such as Qemu or VirtualBox. For example, the following command can be used for this purpose:

Qemu-system-i386 -fda floppy.img

Voila: the system bootloader boot.img, which we integrated into the first sector of the disk image, loads the operating system kernel mykernel.bin, which displays a welcome message. Enter the ls command to obtain the names of two files located on the disk (mykernel.bin and test.bin), then enter the name last file to execute it and display the X symbol on the screen.

This is cool, isn't it? Now you can begin to modify the command shell of your operating system, add implementations of new commands, and also add files additional programs to disk. If you want to run this operating system on a real PC, you should refer to the section “Running the bootloader on a real hardware platform” from the previous article in the series - you will need exactly the same commands. Next month, we'll make our operating system more powerful by allowing downloadable programs to use system functions, introducing a code-sharing concept to reduce code duplication. Much of the work is still ahead.

lib.asm library routines

As mentioned earlier, the lib.asm library provides a large set of useful routines for use within your operating system kernels and individual programs. Some of them use instructions and concepts that have not yet been touched upon in articles in this series, others (such as disk routines) are closely related to the design of file systems, but if you consider yourself competent in these matters, you can read them yourself with their implementations and understand the principle of operation. However, it is more important to figure out how to call them from your own code:

  • lib_print_string - accepts a pointer to a null-terminated string via the SI register and prints the string to the screen.
  • lib_input_string - accepts a pointer to a buffer via the SI register and fills this buffer with characters entered by the user using the keyboard. After the user presses the Enter key, the line in the buffer is null-terminated and control returns to the calling program code.
  • lib_move_cursor - moves the cursor on the screen to a position with coordinates transmitted through the DH (row number) and DL (column number) registers.
  • lib_get_cursor_pos - this subroutine should be called to obtain the current row and column numbers using the DH and DL registers, respectively.
  • lib_string_uppercase - takes a pointer to the beginning of a null-terminated string using the AX register and converts the characters of the string to uppercase.
  • lib_string_length - takes a pointer to the beginning of a null-terminated string via the AX register and returns its length via the AX register.
  • lib_string_compare - accepts pointers to the beginning of two null-terminated strings using the SI and DI registers and compares these strings. Sets the carry flag if the lines are identical (to use a jump instruction depending on the jc carry flag) or clears this flag if the lines are different (to use the jnc instruction).
  • lib_get_file_list - Takes a pointer to the start of a buffer via the SI register and puts into that buffer a null-terminated string containing a comma-separated list of file names from disk.
  • lib_load_file - takes a pointer to the beginning of a line containing the file name using the AX register and loads the contents of the file at the offset passed through the CX register. Returns the number of bytes copied into memory (that is, the file size) using the BX register, or sets the carry flag if a file with the given name is not found.

This series of articles is devoted to low-level programming, that is, computer architecture, the design of operating systems, assembly language programming and related areas. So far, two habrausers are doing the writing - and . For many high school students, students, and even professional programmers, these topics turn out to be very difficult to learn. There is a lot of literature and courses devoted to low-level programming, but it is difficult to get a complete and all-encompassing picture. It is difficult, after reading one or two books on assembly language and operating systems, to at least in general terms imagine how this complex system of iron, silicon and many programs - a computer - actually works.

Everyone solves the learning problem in their own way. Some people read a lot of literature, some try to quickly move on to practice and figure it out as they go, some try to explain to their friends everything they are studying. And we decided to combine these approaches. So, in this course of articles we will demonstrate step by step how to write a simple operating system. The articles will be of a review nature, that is, they will not contain exhaustive theoretical information, but we will always try to provide links to good theoretical materials and answer all questions that arise. We don't have a clear plan, so many important decisions will be made along the way, taking into account your feedback.

We may deliberately stymie the development process in order to allow you and ourselves to fully understand the full consequences of a bad decision, as well as to hone some technical skills on it. So you should not perceive our decisions as the only correct ones and blindly believe us. We emphasize once again that we expect readers to be active in discussing articles, which should greatly influence the overall process of developing and writing subsequent articles. Ideally, over time, I would like some of the readers to join in the development of the system.

We will assume that the reader is already familiar with the basics of assembly and C languages, as well as the elementary concepts of computer architecture. That is, we will not explain what a register or, say, RAM is. If you do not have enough knowledge, you can always turn to additional literature. A short list of references and links to sites with good articles are at the end of the article. It is also advisable to know how to use Linux, since all compilation instructions will be given specifically for this system.

And now - closer to the point. In the rest of the article, we will write the classic “Hello World” program. Our Helloworld will be a little specific. It will not be launched from any operating system, but directly, so to speak, “on bare metal.” Before we start writing the code, let's figure out exactly how we are trying to do this. And for this we need to consider the process of booting the computer.

So, take your favorite computer and press the largest button on the system unit. We see a cheerful screensaver, the system unit happily beeps with its speaker and after some time the operating system loads. As you understand, the operating system is stored on a hard drive, and here the question arises: how did the operating system magically load into RAM and start executing?

Know this: the system that is on any computer is responsible for this, and its name - no, not Windows, tip your tongue - it is called BIOS. Its name stands for Basic Input-Output System, that is, a basic input-output system. The BIOS is located on a small chip on motherboard and starts immediately after pressing the large ON button. BIOS has three main tasks:

  1. Detect all connected devices (processor, keyboard, monitor, RAM, video card, head, arms, wings, legs and tails...) and check their functionality. The POST (Power On Self Test) program is responsible for this. If vital hardware is not detected, then no software will be able to help, and at this point the system speaker will squeak something ominous and the OS will not get to the point at all. Let's not talk about sad things, let's assume that we have a fully working computer, let's rejoice and move on to considering the second BIOS function:
  2. Providing the operating system with a basic set of functions for working with hardware. For example, through BIOS functions you can display text on the screen or read data from the keyboard. That's why it's called the basic input/output system. Typically the operating system accesses these functions through interrupts.
  3. Launching the operating system loader. In this case, as a rule, the boot sector is read - the first sector of the storage medium (floppy disk, HDD, CD, flash drive). The media polling order can be set in BIOS SETUP. The boot sector contains a program sometimes called the primary boot loader. Roughly speaking, the bootloader's job is to start the operating system. The process of loading an operating system can be very specific and highly dependent on its features. Therefore, the primary bootloader is written directly by the OS developers and is written to the boot sector during installation. When the bootloader starts, the processor is in real mode.
The sad news is that the bootloader should only be 512 bytes in size. Why so few? To do this, we need to familiarize ourselves with the structure of the floppy disk. Here is an informative picture:

The picture shows the surface of a disk drive. A floppy disk has 2 surfaces. Each surface has ring-shaped paths (tracks). Each track is divided into small arc-shaped pieces called sectors. So, historically, a floppy disk sector has a size of 512 bytes. The very first sector on the disk, the boot sector, is read by the BIOS into the zero memory segment at offset 0x7C00, and then control is transferred to this address. The bootloader usually loads into memory not the OS itself, but another bootloader program stored on the disk, but for some reason (most likely, this reason is the size) does not fit into one sector. And since for now the role of our OS is played by a banal Helloworld, our main goal is to make the computer believe in the existence of our OS, even if only in one sector, and launch it.

How is the boot sector structured? On a PC, the only requirement for the boot sector is that its last two bytes contain the values ​​0x55 and 0xAA - signatures boot sector. So, it’s already more or less clear what we need to do. Let's write code! The given code is written for the yasm assembler.

Section .text use16 org 0x7C00 ; our program is loaded at address 0x7C00 start: mov ax, cs mov ds, ax ; select the data segment mov si, message cld ; direction for string commands mov ah, 0x0E ; BIOS function number mov bh, 0x00 ; video memory page puts_loop: lodsb ; load the next symbol into al test al, al ; the null character means the end of the line jz puts_loop_exit int 0x10 ; call the BIOS function jmp puts_loop puts_loop_exit: jmp $ ; eternal loop message: db "Hello World!", 0 finish: times 0x1FE-finish+start db 0 db 0x55, 0xAA ; boot sector signature

This short program requires some important explanations. The line org 0x7C00 is needed so that the assembler (meaning the program, not the language) correctly calculates the addresses for labels and variables (puts_loop, puts_loop_exit, message). So we inform him that the program will be loaded into memory at address 0x7C00.
In lines
mov ax, cs mov ds, ax
the data segment (ds) is set equal to the code segment (cs), since in our program both data and code are stored in the same segment.

Next in the loop, the message “Hello World!” is displayed character by character. For this purpose, function 0x0E of interrupt 0x10 is used. It has the following parameters:
AH = 0x0E (function number)
BH = video page number (don’t bother yet, indicate 0)
AL = ASCII character code

At the line "jmp$" the program freezes. And rightly so, there is no need for it to execute extra code. However, in order for the computer to work again, you will have to reboot.

In the line “times 0x1FE-finish+start db 0” the rest of the program code (except for the last two bytes) is filled with zeros. This is done so that after compilation, the last two bytes of the program contain the signature of the boot sector.

We seem to have sorted out the program code, let's now try to compile this happiness. For compilation, we will need, strictly speaking, an assembler - the above-mentioned yasm. It is available in most Linux repositories. The program can be compiled as follows:

$ yasm -f bin -o hello.bin hello.asm

The resulting hello.bin file must be written to the boot sector of the floppy disk. This is done something like this (of course, instead of fd you need to substitute the name of your drive).

$ dd if=hello.bin of=/dev/fd

Since not everyone still has disk drives and floppy disks, you can use virtual machine eg qemu or VirtualBox. To do this, you will have to make a floppy disk image with our bootloader and insert it into the “virtual floppy drive”.
Create a disk image and fill it with zeros:

$ dd if=/dev/zero of=disk.img bs=1024 count=1440

We write our program at the very beginning of the image:
$ dd if=hello.bin of=disk.img conv=notrunc

We launch the resulting image in qemu:
$ qemu -fda disk.img -boot a

After launching, you should see the qemu window with the happy line “Hello World!” This is where the first article ends. We will be glad to see your feedback and wishes.

© 2024 hecc.ru - Computer technology news