[tutorial] how to write your own OS

Pagina: 1 2 Laatste
Acties:
  • 6.696 views sinds 30-01-2008
  • Reageer

Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
Op maandag 27 augustus 2001 02:41 schreef Dawai het volgende:
Die 55AA is de mediacode, een soort handtekening, die vind je altijd op het einde van een DOS/Windows MBR of bootsector. Het is geen code die uitgevoerd wordt en is trouwens overbodig voor een eigen besturingssysteem. Als die code ontbreekt op een MS partitie zal Scandisk komen blaten :)
Niet helemaal waar.. Er zijn bepaalde merken BIOS'en die gaan zeuren om deze 2 bytes waardoor "hardware-matig" er al niets wordt ingeladen. Veel programma's gebruiken deze 55AA om te kijken of iets daadwerkelijk een bootsector is (zoals dus scandisk). Terzijde: soms wordt ook de o-zo-foute E8/EB methode toegepast, dat wil zeggen dat je eerste instructie in de BS een JMP moet zijn.

Weglaten van deze signature zorgt er alleen maar voor dat je OS op een flink aantal (oude) systemen niet meer opstart. Ik denk dat op het moment dat je 510 bytes aan code in je BS hebt staan, je vast wel ergens 2 bytes kan optimizen :)



Maar ben weer net terug van "vakantie". Ik hoop volgende week een nieuw hoofdstuk af te hebben..

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

  • KompjoeFriek
  • Registratie: Maart 2001
  • Laatst online: 11-06 04:49

KompjoeFriek

Statsidioot

Ik wil die tutorial ook zeker lezen, alleen al om te weten te komen hoe het nou precies werkt.

En misschien als ik me er in ge verdiepen, dat er nog een leuk OS-je uit komt.

ja, lijkt me heel leerzaam.

WhatPulse! - Rosetta@Home - Docking@Home


Acties:
  • 0 Henk 'm!

  • JoostBaksteen
  • Registratie: December 2000
  • Laatst online: 11-06 19:01
Op zondag 02 september 2001 21:43 schreef JayTaph het volgende:

[..]
Ik hoop volgende week een nieuw hoofdstuk af te hebben..
Is ie al af?? Kan niet wachten :)

Acties:
  • 0 Henk 'm!

  • Super_ik
  • Registratie: Maart 2001
  • Laatst online: 23:04

Super_ik

haklust!

:9~ waar blijft het volgende deel :9~

8<------------------------------------------------------------------------------------
Als ik zo door ga haal ik m'n dood niet. | ik hou van goeie muziek


Acties:
  • 0 Henk 'm!

  • _the_crow_
  • Registratie: September 2000
  • Laatst online: 30-03 14:35

_the_crow_

Rare vogel

ik heb nu een deel geprogged van me OS! waar blijft het volgende deel??

Schrödingers cat: In this case there are three determinate states the cat could be in: these being Alive, Dead, and Bloody Furious.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

hmmm
"Operating Systems: Design and Implementation - Andrew S. Tanenbaum / Albert S Woodhull"

Fijne 940 paginas over de interne werking van minix, plus source :) Een must-read als je iets van plan bent met OSs.

Acties:
  • 0 Henk 'm!

  • sus
  • Registratie: September 2000
  • Laatst online: 06:09

sus

is druk :+

Sorry dat ik hem omhoog schop, maar is het volgende deel er al?? Ik ben razend benieuwd :9~

Acties:
  • 0 Henk 'm!

  • _the_crow_
  • Registratie: September 2000
  • Laatst online: 30-03 14:35

_the_crow_

Rare vogel

wil JayTaph deze topic ff afmaken plz!
er zijn mensen die hier veel aan hebben!

Schrödingers cat: In this case there are three determinate states the cat could be in: these being Alive, Dead, and Bloody Furious.


Acties:
  • 0 Henk 'm!

  • Philip_Janssen
  • Registratie: Juni 2001
  • Laatst online: 06-06 12:33
Op maandag 30 juli 2001 12:04 schreef het_beest het volgende:
Ik wil best een 2e Bill Gates worden, COUNT ME IN!
Waarom hoop ik nou dat jij minder fouten in je OS laat zitten >:)

Acties:
  • 0 Henk 'm!

  • reddog33hummer
  • Registratie: Oktober 2001
  • Laatst online: 21-04 21:04

reddog33hummer

Dat schept mogelijkheden

het is wel jammer dat al die boeken of de unix/linux implementaties gaan. Ik ben benieuwd hoe NT in elkaar zit

Backup not found (R)etry (A)bort (P)anic<br\>AMD 3400+ 64, 2 GB DDR, 1,5 TB Raid5


Acties:
  • 0 Henk 'm!

Anoniem: 12353

Op zondag 11 november 2001 18:08 schreef reddog33hummer het volgende:
het is wel jammer dat al die boeken of de unix/linux implementaties gaan. Ik ben benieuwd hoe NT in elkaar zit
Dit naslag werkje niets voor jou dan?
http://www.sysinternals.com/insidew2k.shtml

Acties:
  • 0 Henk 'm!

Anoniem: 37864

Dit naslag werkje niets voor jou dan?
http://www.sysinternals.com/insidew2k.shtml
Dat boek heb ik ook gelezen en het is echt helemaal fantastisch :) Het gaat duidelijk en redelijk diep in op hoe dingen werken en vooral waarom ze zo werken. Er wordt niet gekeken naar andere oplossingen, dus als je een boek zoekt wat beschrijft waarom bijvoorbeeld NT's memory manager een stuk beter is dan die van Linux en waarom NT qua ontwerp eigenlijk sowieso een stuk mooier in elkaar zit dan Linux heb je er weinig aan (-5. Flamebait) :?

Een ander alternatief wat interessant is is 'Operating system concepts'. Dit boek gaat wel in op Linux EN NT(4) en behandelt verder algemene OS dingen. Het beschrijft zonder echt een oordeel te geven gewoon meerdere manieren om dingen aan te pakken en waarom je dat zo kan doen. Ook hier weer geen technisch verhaal; daarvoor heb je de programmeerhandleidingen van je PC (of andere veel 1337ere hardware :)

Acties:
  • 0 Henk 'm!

  • rjsomeone
  • Registratie: Juli 2001
  • Laatst online: 20-11-2023

rjsomeone

a.k.a. Tuslsh

Misschien beetje een oud topic, maar is er al iets meer dan het 1e hoofdstuk? of is 't project doodgebloed?

Hier had uw advertentie kunnen staan :).


Acties:
  • 0 Henk 'm!

Anoniem: 44042

Waar kan je eigenlijk de tutorial bekijken of downloaden?

Acties:
  • 0 Henk 'm!

Anoniem: 4552

Op donderdag 11 april 2002 21:18 schreef Molshoop het volgende:
Waar kan je eigenlijk de tutorial bekijken of downloaden?
Als je eens de moeite leest om het topic door te lezen was je hem gewoon tegen gekomen |:(

Acties:
  • 0 Henk 'm!

  • rjsomeone
  • Registratie: Juli 2001
  • Laatst online: 20-11-2023

rjsomeone

a.k.a. Tuslsh

Op donderdag 11 april 2002 21:22 schreef CoDeR het volgende:

[..]

Als je eens de moeite leest om het topic door te lezen was je hem gewoon tegen gekomen |:(
Ik heb het hele topic gelezen, en ben alleen h1 tegengekomen, geen link of wat dan ook??
Of kijk ik nou weer niet uit m'n doppen :)?

Hier had uw advertentie kunnen staan :).


Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
Ik wil het heel graag afmaken.. maar helaas... geen tijd geen tijd geen tijd..

Maar ik geloof dat er ondertussen hier op GOT een aantal "gevorderde coders" erbij zijn gekomen die ook in staat zijn om een OS te schrijven.. misschien dat die zich geroepen voelen om verder dingetjes uit te leggen..

En anders staan er genoeg links her en der met verschrikkelijk veel info.

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

Anoniem: 4552

Op donderdag 11 april 2002 21:47 schreef rjsomeone het volgende:

[..]

Ik heb het hele topic gelezen, en ben alleen h1 tegengekomen, geen link of wat dan ook??
Of kijk ik nou weer niet uit m'n doppen :)?
Pardon :o, ik dacht dat het om hoofdstuk 1 ging :Z

Acties:
  • 0 Henk 'm!

Anoniem: 19151

Ik heb eigenlijk ook geen tijd om echt een mooie tutorial te maken omdat ik al m'n tijd al steek m'n eigen OS. Maar er zijn op newsgroups zoals alt.os.development wel veel mensen die bezig zijn met tutorials. Het was een beetje de bedoeling dat ze werden verzameld op een site maar meestal lukte dat niet echt ;). Op http://www.osjournal.n3.net/ staan ook een paar van die tutorials bij elkaar en ze proberen nog steeds meer artikels te vinden. Zo zijn er nog een paar sites waar veel bij elkaar staat (Operating Systems for Dummies) en Bona Fide OS Development (http://osanswers.port5.com/index.php).

Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
Jeej.. een nieuw deel is af :)

Ik heb ze ook online gezet omdat er een flink aantal mensen dat graag wilde hebben: http://www.xs4all.nl/~joshua/tutorial/


En nu part chapter :)

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
Bootstrapping Deel 2: Het echte(re) werk
----------------------------------------

Moeilijkheidsgraad: matig
Voorkennis: i386 assembly, FAT12
Referenties: Ralfs Brown Interrupt List
Tools: nasm


Zoals eerder gezegd gaan weg een MS-DOS compatible floppy maken met een eigen bootsector. Hierdoor is het mogelijk om onze schijfjes met eigen OS te voorzien onder windows, linux, DOS of praktisch elk ander OS. We gebruiken het FAT12 filesysteem, mede omdat het erg simpel is, en erg effectief (voor the time being in ieder geval). Praktisch bezwaar aan de FAT12 is natuurlijk de 8.3 bestandsnamen. Maar ik denk dat we daar in ieder geval ons niet druk over moeten maken. Zodra je je eigen OS gaat ontwerpen, ga je je daar natuurlijk wel druk over maken.. :)


Ok, hoe ziet een FAT12-schijfje eruit?
Formateer een normale floppy onder linux, windows of DOS. Maar zorg er wel voor dat hij FAT12 formateerd (mkfs.msdos -F 12 /dev/fd0). Na het formateren hebben we een FAT12-schijfje, compleet met DOS-bootsector en FAT-blokken. Voor de complete layout van FAT12 moet je zelf eventjes de google gebruiken. Ik kan en ga zeker niet alles uitleggen (geld terug Bert!)



Let er op dat een FAT12-bootsector extra informatie heeft over het schijfje. Deze info wordt de BPB (BIOS Parameter Block) genoemd en ziet er zo uit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
    BytesPerSector      dword
    SectorsPerCluster   dbyte
    ReservedSectors     dwword
    NumberOfFATs        dbyte
    RootEntries     dword
    TotalSectors        dword
    Media           dbyte
    SectorsPerFAT       dword
    SectorsPerTrack     dword
    HeadsPerCylinder    dword
    HiddenSectors       ddouble
    TotalSectorsBig     ddouble

Deze BPB begint op offset 11 in de bootsector. De rest van de bootsector ziet er alsvolgt uit:
code:
1
2
3
4
5
6
7
8
9
10
11
    3-bytes jumpinstructie naar eigenlijke bootsector-code
    8-bytes OEM Name 
    BPB structure
    1-byte drive letter
    1-byte ongebruikt
    1-byte bootsignature
     4-bytes    serial number
    11-bytes volume naam
    8-bytes filesystem naam ("FAT12   ", of "MYOWNOS " ofzow)
    448 bytes aan bootcode
    2 bytes bootsector terminator (0xAA55)

Zoals je ziet, heb je in een FAT12-schijfje al heel wat minder ruimte voor je bootsector code. Je snapt natuurlijk wel dat deze dus uitermate geoptimaliseerd moet zijn wil je alles voor elkaar krijgen wat je wilt doen in je bootsector. Een veelgebruikte truuk is om in de bootsector een soort van tweede (of zelfs derde) bootsector te laden, waardoor je ineens 1024 bytes (of zelfs nog meer) tot je beschikking krijgt. Veel OS'en waarin vaak veel moet gebeuren voordat de echte kernel geladen kan worden (zoals linux of windows) gebruiken deze truukjes volop.

Een bootsectortje die de naam van de schijf laat zien tijdens het opstarten. Het blijft simpele code:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
[bits 16]           ; 16 bits code  
    org 0       ; We staan op offset 0

    jmp End_Of_BootData

        OEMName         db   "MyOwnOS "
        BytesPerSector      dw  0x0200
        SectorsPerCluster   db  0x01
        ReservedSectors     dw  0x01
        NumberOfFATs        db  0x02
        RootEntries     dw  224
        TotalSectors        dw  2880
        Media           db  0xF0
        SectorsPerFAT       dw  0x09
        SectorsPerTrack     dw  0x0012
        HeadsPerCylinder    dw  0x0002
        HiddenSectors       dd  0x00000000
        TotalSectorsBig     dd  0x00000000

        DriveNumber     db  0x00
                    db  0x00
        ExtBootSignature    db  0x00
        SerialNumber        dd  0x12345678
        VolumeLabel     db  "BLAATAAP   "
        FileSystem      db  "FAT12   "

End_Of_BootData:

    ; We weten niet of we op 0000:7C00 of 07C0:0000 staan, daarom kiezen
    ; we er zelf een.

    mov ax, 0x7C0   ; Segment 07C0
    mov ds, ax      ; Data staat op 07C0:0000 ipv 0000:7C00

    xor ax, ax      ; Stack Segment Goedzetten
    mov ss, ax
    mov sp, 0xFFFF

    mov [bootdrive],dl  ; Onthoud de bootdrive

    ; De volgende code maakt van het duffe zwarte achtergrond een 
    ; mooi blauw kleurtje.
    mov dx,0x3c8    ; VGA Palette register (werkt dus alleen
    xor al,al       ; op een VGA)
    out dx,al       ; We willen kleur 0 (background) instellen
        
    inc dx      ; VGA Data register (0x3c9)
    mov al,8
    out dx,al       ; Output rood component
    xor al,al
    out dx,al       ; Output groen component
    mov al,32
    out dx,al       ; Output blauw component

    dec dx      ; En doe hetzelfde voor kleur 1
    mov al,1
    out dx,al

    inc dx
    mov al,48
    out dx,al
    out dx,al
    mov al,48
    out dx,al

    ; Nu hebben we een mooi kleurtje, print de string op
    ; het beeld.

    mov si, msg         ; Print bericht
    mov cx, 0           ; 'oneindig' (max 65535) karakters
                    ; maximaal
    call    PrintMsg

    mov si, VolumeLabel     ; Print string op het beeld
    mov cx, 11          ; Maximaal 11 karakters
    call    PrintMsg

    xor ah,ah       ; Wacht op een toets
    int 0x16

    int  0x19       ; En reboot, de rest komt later...

; ------------------------------------------------------------------------
; Routine die een string in SI print die eindigt op een 0.
; In: CX = maximale lengte om af te drukken
PrintMsg:
    cld
    lodsb           ; Laad uit DS:SI naar AL
    cmp al,0
    jz  End_Of_String   ; Is het een 0, dan einde
    dec cx      ; Maximaal aantal karakters geprint
    jz  End_Of_String
    call    PrintChar   ; Print character
    jmp PrintMsg    ; En volgende character
End_Of_String:
    ret

; -------------------------------------------------------------------------
; Routine die een karakter in AL op het scherm print. Deze routine maakt 
; gebruik van BIOS-interrupt 0x10 om een karakter te plotten.
PrintChar:
    mov ah,0x0E     ; BIOS functie 15
    mov bx,0x0001   ; Print op page 0 in kleur 1
    int 0x10
    ret

; -------------------------------------------------------------------------
    ; Data die we gebruiken in de bootsector
    msg     db  'hello bootsector. The volume name is: ',0
    bootdrive   db  0

    times 510-($-$$) db 0x90    ; Opvullen met NOP (doet niets)
    dw  0xAA55          ; En de boot terminator

Veel is er ook weer niet veranderd. Eerst word over de data gesprongen, en vandaar uit is het allemaal eigenlijk hetzelfde. Het enige verschil is dat de PrintMsg routine nu in het CX-register een maximaal aantal tekens bevat die geprint wordt. Dit omdat de volumenaam niet op een 0 eindigd. Maar aangezien we weten dat de volnaam maximaal 11 characters groot kan zijn, printen we er ook maar maximaal 11 af.




Lezen van sectoren van schijf naar geheugen:
--------------------------------------------

Een stukje uit de Ralf Brown's Interrupt Lists:

Interrupt 13, functie 02
Read disk sectors into memory
AH = 02
AL = number of sectors to read (non-zero)
CH = low eight bits of cylinder
CL = sector number 1-63 (bits 0-5)
high two bits of cylinder (bits 6-7, hard disk only)
DH = head number
DL = drive number (bit 7 set for hard disk)
ES:BX -> data buffer

Return: CF set on error
if AH=11h (corrected ECC error), AL = burst length
CF clear if successfull
AH = status
AL = number of sectors transferred

Deze functie gebruiken we om sectoren te lezen vanaf de schijf.


Uitlezen van de FAT12 root-directory.
-------------------------------------
Om een root-directory uit te lezen, moeten we eerst weten hoe deze precies is opgebouwd.

Hierna gaan we bekijken op welke sector op de schijf de root-directory begint. Dit is een beetje rekenwerk met de BPB informatie.

RootDirectoryStart = (TotalNrOfFATs * SectorsPerFAT) + ReservedSectors

Elke root-entry is 32 bytes groot. Een snelle rekensom leert ons dat we maximaal BPB.RootEntries * 32 / BPB.BytesPerSector moeten inlezen voordat de gehele root-directory in het geheugen staat. Een andere manier van oplossen, is om sector voor sector in te laden. Dit scheelt geheugen (aangezien we maar maximaal 1 sector tegelijkertijd in het geheugen hebben), en als we een file moeten laden en deze staat in de eerste rootentry-sector, dan hoeven we alle andere sectoren niet in te laden (wat weer een snelheidswinst is).

Zodra de rootentries zijn ingelezen gaan we ze allemaal af en drukken we ze af in een soort van directory-listen a la MS-DOS. Daarna wachten we weer netjes op een toets om vervolgens te rebooten.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
[bits 16]           ; 16 bits code  
    org 0       ; We staan op offset 0

    jmp End_Of_BootData

        OEMName         db   "MyOwnOS "
        BytesPerSector      dw  0x0200
        SectorsPerCluster   db  0x01
        ReservedSectors     dw  0x01
        NumberOfFATs        db  0x02
        RootEntries     dw  224
        TotalSectors        dw  2880
        Media           db  0xF0
        SectorsPerFAT       dw  0x09
        SectorsPerTrack     dw  0x0012
        HeadsPerCylinder    dw  0x0002
        HiddenSectors       dd  0x00000000
        TotalSectorsBig     dd  0x00000000

        DriveNumber     db  0x00
                    db  0x00
        ExtBootSignature    db  0x00
        erialNumber     dd  0x12345678
        VolumeLabel     db  "BLAATAAP   "
        FileSystem      db  "FAT12   "

End_Of_BootData:

    ; We weten niet of we op 0000:7C00 of 07C0:0000 staan, daarom 
    ; kiezen we er zelf een.
    mov ax, 0x07C0      ; Segment 07C0
    mov ds, ax          ; Data staat op 07C0:0000
    jmp 0x07C0:Relocation
Relocation:             ; En de code nu ook

    ; Setup Stack
    cli             ; Geen interrupts
    mov ax, 0x9000
    mov ss,ax
    mov sp, 0xFFFF
    sti             ; Nu mag het weer

    ; Onthoud de bootdrive
    mov [bootdrive],dl

    ; Print a bootmessage
    mov si, BootMsg 
    call    PrintMsg

    ; Reset disk system
    xor ah,ah
    mov dl, [bootdrive]
    int 0x13

    ; -------------------------------------------------------
    ; Lees all FAT sectoren in op 1000:0000
    xor ax, ax
    mov al, [NumberOfFATs]
    mov cx, [SectorsPerFAT]
    mul cx
    mov cx, ax
    mov bp, [ReservedSectors]
    call     ReadSectors

    ; -------------------------------------------------------
    ; Lees de root-entry in op 2000:0000
    mov ax, 0x0000
    mov [Cur_Off], ax
    mov ax, 0x2000
    mov [Cur_Seg], ax

    xor dx, dx
    mov ax, [RootEntries]
    shl ax, 5               ; Maal 32
    div word [BytesPerSector]       ; ax = ax:dx / bytespersector
    mov cx, ax              ; Aantal sectoren
    call    ReadSectors         ; BP already set correctly

    mov [DataStartSector], bp

    ; -------------------------------------------------------
    ; Zoek naar een kernel.sys
    mov bp, [RootEntries]
    mov ax, 0x2000
    mov es, ax
    push    ds
    mov ds, ax
    xor si, si
ShowNextEntry:
    push    si
    push    bp

    xor al,al           ; Niks laten zien als ie leeg is
    cmp [es:si], al
    je  SkipEmptyEntry

    cmp [es:si+0x1A], al    ; Long File Name Entry
    je  SkipEmptyEntry      

    mov cx, 12          ; maximaal 11 karakters printen
    call    PrintMsg
    mov al, 10          ; Linefeed
    call    PrintChar
    mov al, 13          ; en carriage return
    call    PrintChar

SkipEmptyEntry:
    pop bp
    pop si

    add si, 32
    dec bp          ; Alle entries al gehad?
    jnz ShowNextEntry

    pop ds

DeadLock:
    jmp DeadLock

; ------------------------------------------------------------------------
; Routine die een string in DS:SI print die eindigt op een 0. 
; In: CX = maximale lengte om af te drukken
PrintMsg:
    cld
    lodsb           ; Laad uit DS:SI naar AL
    cmp al,0
    jz  End_Of_String   ; Is het een 0, dan einde
    dec cx      ; Maximaal aantal karakters geprint?
    jz  End_Of_String
    call    PrintChar   ; Print character
    jmp PrintMsg    ; En volgende character
End_Of_String:
    ret


; -------------------------------------------------------------------------
; Routine die een karakter in AL op het scherm print. Deze routine maakt 
; gebruik van BIOS-interrupt 0x10 om een karakter te plotten.
PrintChar:
    mov ah,0x0E     ; BIOS functie 15
    mov bx,0x000    ; Print op page 0 in kleur 7
    int 0x10
    ret

; -------------------------------------------------------------------------
; Routine die een of meerdere sectoren inleest
; CX = aantal sectoren
; BP = startsector (LBA)
ReadSectors:
    push    cx
    mov di, 5
RS_Retry:
    call    ConvertLBA2CHS
    mov dl, [bootdrive]
    mov bx, [Cur_Seg]       ; Data wordt in CurSeg:CurOff 
    mov es, bx          ; geladen.
    mov bx, [Cur_Off]
    mov ax, 0x0201
    int 0x13
    jnc RS_GoodLoad

    mov al, 'X'         ; Niet goed? Print een X
    call    PrintChar

    dec di          ; Al 5 keer geprobeerd? Dan 
    jnz RS_Retry        ; kappen we ermee...

    mov al, '!'
    call    PrintChar
    jmp $           ; Deadlock

RS_GoodLoad:
    mov bx, [Cur_Off]       ; Increase memory offset
    add bx, 512
    mov [Cur_Off], bx

    inc bp          ; Next LBA sector

    pop cx
    loop    ReadSectors
    ret

; -------------------------------------------------------------------------
; Convert LBA to CHS
; in : BP = LBA sector
; out: ch = cylinder  
;   dh = head
;   cl = sector (high cylinder not used)
ConvertLBA2CHS:
    mov ax, bp
    xor dx, dx
    div word [SectorsPerTrack]
    inc dl              ; Sectors starts at 1, not 0
    mov bl, dl

    xor dx, dx
    div word [HeadsPerCylinder]

    mov ch, al       ; Cylinder
    mov cl, bl      ; Sectors
    mov dh, dl      ; Head
    ret

; -------------------------------------------------------------------------
    ; Data die we gebruiken in de bootsector
    BootMsg     db  'Booting...', 0
    ErrorMsg    db  'Cannot find kernel.sys', 13, 10, 0
    DoneMsg     db  'done!',0

    kernel      db  'KERNEL  SYS'

    DataStartSector dw  0
    bootdrive   db  0
    Cur_Seg     dw  0x1000
    Cur_Off     dw  0x0000

    times 510-($-$$) db 0x90    ; Opvullen met NOP (doet niets)
    dw  0xAA55          ; En de boot terminator

Schrijf de binary weg op een geformateerde floppy, zet er een aantal test-bestandjes en directories (bestanden IN die directories worden dus niet getoond, aangezien deze niet in de root-directory staan) en kijk hoe goed het (wel|niet) werkt..

Een leuke opdracht zou zijn om ervoor te zorgen dat ook lange bestandsnamen kunnen worden afgedrukt. Op http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/vfat.html vind je een hoop info om zelf hiermee aan de slag te gaan.




Lezen van een file vanaf schijf naar het geheugen
-------------------------------------------------
Dit is niet zo heel erg veel meer dan het lezen van de directory. Als eerste moeten we natuurlijk de rootdirectory lezen om te kijken of de file uberhaupt bestaat. Hebben we iets gevonden dat voldoet aan onze bestandsnaam (in ons geval: KERNEL.SYS), dan laden we deze file in door alle clusters van de file in te lezen. Voor info over het inlezen van clusters kun je het beste eventjes wat extra documentatie raadplegen (het stelt niet zo veel voor, het is eigenlijk een soort van linklist).


Onze "hello world" kernel
-------------------------
Als eerste gaan we even een simpele kernel bouwen. Schrik niet, want wat deze doet is niets meer dan wat text op het beeldscherm zetten op precies dezelfde manier als in de bootsector (met interrupt 10 dus). Ook zal deze kernel in assembler geschreven worden, aangezien we nog heeeeel veel moeten doen voordat we ook maar een C-kernel kunnen inladen. :(

Waarom kunnen we nog geen C-kernel inladen?
Dat heeft een paar redenen. De belangrijkste reden is wel dat onze compiler (gcc/djgpp) alle code in 32bit assembleerd, en wij in ons OS nog steeds in 16 bit werken. Verder heeft C de eigenschap dat veel functies (strcpy, printf, memcpy etc) specifieke functies zijn per OS, en dat deze worden opgeslagen in de C-lib. We moeten dus voordat we ook maar 1 printf'je kunnen gebruiken een compleet eigen C-library moeten schrijven. Gelukkig is dit niet zo heel moeilijk, maar het is wel een langdradig karwei (het leven van een OS-coder is niet altijd feest). Zodra we deze 2 "problemen" hebben opgelost, kunnen we beginnen met het schrijven van de eerste echte C-kernels.

Goed, de assembler-kernel dan maar voorlopig. Wat komt erin te staan?
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
[bits 16]           ; 16 bits code  
    org 0       ; We staan op offset 0

    mov ax, 0x0050
    mov ds, ax
    mov es, ax

    mov si, msg
    call     PrintMsg

    xor bx,bx       ; BX houd de huidige kleur vast

NextLoop:
    ; Wacht op de monitor totdat deze klaar is met het maken
    ; van horizontale lijn. Dit is een soort van vertragings-lus
    ; die we inbouwen en hierdoor lijkt het alsof er allemaal kleine 
    ; balkjes in de achtergrond liggen.
    mov dx,0x3da
NHR:
    in  al,dx
    test    al,1
    jne NHR
HR:
    in  al,dx
    test    al,1
    je  HR

    mov dx,0x3c8    ; Achtergrond kleur veranderen
    xor al,al
    out dx,al
    inc dx
    mov al,[r]
    out dx,al
    mov al,[g]
    out dx,al
    mov al,[b]
    out dx,al

    add byte [r], 3

    jmp NextLoop    ; Dit gaan oneindig door... :)

; ------------------------------------------------------------------------
; Routine die een string in SI print die eindigt op een 0. 
PrintMsg:
    cld
    lodsb           ; Laad uit DS:SI naar AL
    cmp al,0
    jz  End_Of_String   ; Is het een 0, dan einde
    call    PrintChar   ; Print character
    jmp PrintMsg    ; En volgende character
End_Of_String:
    ret

; -------------------------------------------------------------------------
; Routine die een karakter in AL op het scherm print. Deze routine maakt 
; gebruik van BIOS-interrupt 0x10 om een karakter te plotten.
PrintChar:
    mov ah,0x0E     ; BIOS functie 15
    mov bx,0x0001   ; Print op page 0 in kleur 1
    int 0x10
    ret

; -------------------------------------------------------------------------
    ; Data die we gebruiken in de bootsector
    msg db 13,10,13,10,"hallo! Dit is onze eerste kernel!",13,10
    db 13,10
    db "We doen hierin nog niet zo heel erg veel, behalve leuke",13,10
    db "kleurtjes en deze text laten zien op het scherm.",13,10
    db 13,10
    db "Mocht je zelf nog leuke ideetjes hebben, dan mag je dat ",13,10
    db "dat altijd zelf uitproberen...",13,10
    db 13,10
    db 0

    ; Rood, Groen en Blauw component
    r   db 0
    g   db 0
    b   db 0

    ; De kernel is niet gebonden aan een grootte. Zorg er alleen voor dat
    ; deze kernel niet boven de 64KB uitkomt ivm offset-wrapping tijdens
    ; het laden.
    times 2345 db 0x90
    db "blaat"

Niet veel dus. Een simpel bericht op het beeldscherm en wat gepiel met kleurtjes. Als je echt teveel vrije tijd hebt, dan kan je hier vandaan een soort van bootloader in elkaar zetten: zorg dat de bootsector alle files in de root laat zien, en laat de gebruiker hieruit 1 kiezen. Mocht je dit niet halen qua code, dan kun je altijd nog een extra bootloader laden. Zorg dat je bootsector STAGE2.SYS laad, en je hebt in stage2 alle ruimte voor je loader (wat dacht je van een leuk plaatje op de achtergrond, scrollertje onderdoor etc). :)

ok, onze bootsector om KERNEL.SYS in te laden en uit te voeren:<blockquote><font size="1" face="verdana, arial, helvetica">code:</font><hr><font face="courier, fixedsys, lucida console"><nobr>[bits 16]                  ; 16 bits code      
      org      0            ; We staan op offset 0

      jmp      End_Of_BootData

            OEMName                  db       "MyOwnOS "
            BytesPerSector            dw      0x0200
            SectorsPerCluster      db      0x01
            ReservedSectors            dw      0x01
            NumberOfFATs            db      0x02
            RootEntries            dw      224
            TotalSectors            dw      2880
            Media                  db      0xF0
            SectorsPerFAT            dw      0x09
            SectorsPerTrack            dw      0x0012
            HeadsPerCylinder      dw      0x0002
            HiddenSectors            dd      0x00000000
            TotalSectorsBig            dd      0x00000000

            DriveNumber            db      0x00
                              db      0x00
            ExtBootSignature      db      0x00
            erialNumber            dd      0x12345678
            VolumeLabel            db      "BLAATAAP   "
            FileSystem            db      "FAT12   "

End_Of_BootData:

      ; We weten niet of we op 0000:7C00 of 07C0:0000 staan, daarom 
      ; kiezen we er zelf een.
      mov      ax, 0x07C0            ; Segment 07C0
      mov      ds, ax                  ; Data staat op 07C0:0000
      jmp      0x07C0:Relocation
Relocation:                        ; En de code nu ook

      ; Setup Stack
      cli                        ; Geen interrupts
      mov      ax, 0x9000
      mov      ss,ax
      mov      sp, 0xFFFF
      sti                        ; Nu mag het weer

      ; Onthoud de bootdrive
      mov      [bootdrive],dl

      ; Print a bootmessage
      mov      si, BootMsg 
      call      PrintMsg

      ; Reset disk system
      xor      ah,ah
      mov      dl, [bootdrive]
      int      0x13

      ; -------------------------------------------------------
      ; Lees all FAT sectoren in op 1000:0000
      xor      ax, ax
      mov      al, [NumberOfFATs]
      mov      cx, [SectorsPerFAT]
      mul      cx
      mov      cx, ax
      mov      bp, [ReservedSectors]
      call       ReadSectors

      ; -------------------------------------------------------
      ; Lees de root-entry in op 2000:0000
      mov      ax, 0x0000
      mov      [Cur_Off], ax
      mov      ax, 0x2000
      mov      [Cur_Seg], ax

      xor      dx, dx
      mov      ax, [RootEntries]
      shl      ax, 5                        ; Maal 32
      div      word [BytesPerSector]            ; ax = ax:dx / bytespersector
      mov      cx, ax                        ; Aantal sectoren
      call      ReadSectors                  ; BP already set correctly

      mov      [DataStartSector], bp

      ; -------------------------------------------------------
      ; Zoek naar een kernel.sys
      mov      bp, [RootEntries]
      mov      ax, 0x2000
      mov      es, ax
      xor      di, di
CheckNextEntry:
      pusha
      mov      si, kernel            ; check naar KERNEL  SYS
  &nb

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

  • D2k
  • Registratie: Januari 2001
  • Laatst online: 03-02 23:18

D2k

JayTaph +1 behulpzaam
btw: ik zal bij de volgende update je site in de faq opnemen
<edit>
gelijk maar ff gedaan ;) je staat erin
</edit>

Doet iets met Cloud (MS/IBM)


Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
Op dinsdag 30 april 2002 22:07 schreef D2k het volgende:
JayTaph +1 behulpzaam
btw: ik zal bij de volgende update je site in de faq opnemen
<edit>
gelijk maar ff gedaan ;) je staat erin
</edit>
Da's leuk, maar euh.. site wil ik het niet noemen. Het zijn alleen maar wat filetjes en no way dat ik hiervoor een site ga opzetten :P

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

Anoniem: 34259

Aaarghh begrijp hier echt totaal niks van :( :(
Moet toch maar eens goed assembly gaan leren

Acties:
  • 0 Henk 'm!

  • D2k
  • Registratie: Januari 2001
  • Laatst online: 03-02 23:18

D2k

Op woensdag 01 mei 2002 09:24 schreef JayTaph het volgende:

[..]

Da's leuk, maar euh.. site wil ik het niet noemen. Het zijn alleen maar wat filetjes en no way dat ik hiervoor een site ga opzetten :P
ach je staat erin ;) daar ging het om :P

Doet iets met Cloud (MS/IBM)


Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
Fsck, I seemed to have misplaced my old OS :( (lees: ergens diep ver weg op een cdrommetje die waarschijnlijk niet meer leesbaar is door vuil en krassen)..

Dat wordt dus ook voor mij overnieuw beginnen :(

*help* :+

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

Anoniem: 34259

Op woensdag 01 mei 2002 20:26 schreef JayTaph het volgende:
Fsck, I seemed to have misplaced my old OS :( (lees: ergens diep ver weg op een cdrommetje die waarschijnlijk niet meer leesbaar is door vuil en krassen)..

Dat wordt dus ook voor mij overnieuw beginnen :(

*help* :+
Even apeldoorn bellen :)

Acties:
  • 0 Henk 'm!

  • Grum
  • Registratie: Juni 2001
  • Niet online
Vuil & krassen zijn altijd wel weg te polijsten mits ze aan de 'data' kant zitten :)

Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
Op woensdag 01 mei 2002 20:37 schreef Grum het volgende:
Vuil & krassen zijn altijd wel weg te polijsten mits ze aan de 'data' kant zitten :)
Ware het niet dat ik ook niet weet op *welke* cd het eventueel op kan staan (lange leve de edding :+)

Ach misschien kom ik em nog tegen... Maar ik denk dat ik weer aan de slag ga aan een eigen stdlib ofzo.. |:(..

printf, here i come.. :+

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

Anoniem: 34041

Leuk topic om te lezen maar ik snap er niks van :). Ik ga toch maar even wat meer info zoeken nasm enzovoort. Want een makkelijke moeilijkheidsgraad bestaat niet in dit topic voor mij :).

Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
Protect Mode: Where the real fun begins
---------------------------------------

Moeilijkheidsgraad: moeilijk
Voorkennis: i386 assembly, protected mode
Referenties: Ralfs Brown Interrupt List, Intel x86 Docs
Tools: nasm

Note: ik ga *NIETS* uitleggen over protected mode. Niet alleen omdat dat veel werk is, maar ook omdat ik een redelijke kennis verwacht van de mensen die een OS willen bouwen. Het kan zijn dat je wat dingen mist, of dingen gewoon niet snapt (dat mag), en daarvoor staat vragen vrij. Maar termen zoals selectors, descriptors, granularity en de verschillen tussen logical, physical en virtual memory moeten duidelijk zijn (en is dat niet helemaal, dan mag je vast wel een of twee topics openen van de modjes alhier :)).


Op dit moment hebben we een bootsector die een (willekeurige) file van een ms-dos schijfje leest. Alles in real mode natuurlijk, omdat een (intel) processor automatisch opstart in real mode, dus we hoeven hiervoor niets speciaals te doen, en bijkomend voordeel: we kunnen de BIOS functies gebruiken om van schijf te lezen, en op het beeld te schrijven.
Kan protected mode dat dan niet (leem hoor)? Jawel, alleen moeten wij daar zelf zorg voor dragen want de BIOS van een systeem (althans, de functies die wij gebruiken) is 16bit realmode code is en niet direct aanspreekbaar is vanuit protected mode.

What's next?

Nu moeten we overschakelen naar protected mode, zodat we onze 32bits kernel straks kunnen starten. Om over te schakelen hebben we nog een stukje assembler nodig, de zogenaamde head of trampoline-functies. Dit stukje code wordt (in dit voorbeeld) niet geplaatst in de boot-sector, aangezien daar geen plek meer vrij voor is, maar staat vooraan in de kernel.sys die we met de bootsector inladen.
De kernel.sys file bestaat dus uit 2 afzonderlijke delen die we aan elkaar plakken: de head en de echte kernel. Deze 2 file's assembleer/compile ik afzonderlijke van elkaar, en plak ze stomweg aan elkaar zodat 1 grote kernel.sys ontstaat.

(deze code vereist dat je de boot4.asm file gebruikt als bootsector, daar zijn ook meteen een paar lompe fouten mbt het inladen van clusters verholpen ;))
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
; This piece of code is the start of the kernel. It is a bit of ASM which sets
; up the whole system so the second part of the kernel (the C-bit), can start
; without problems. It's almost the same method used as in the linux kernel
; (head.s)
[bits 16]
    org  0

    mov ax, KERNEL_OFF  ; Kernel starts at 0050:0000
    shr ax, 4
    mov ds, ax
    mov es, ax
    mov gs, ax

    call    DetectCPU
    call    EnableA20

    ; From here, we can use 386 code safely...

    ; Set up GDT
    xor eax, eax    ; Calculate address of the GDT on the fly
    mov ax, ds
    shl eax, 4
    add [GDT+2], eax

    cli         ; Load the GDT
    lgdt    [GDT]

    mov eax, cr0    ; Set PE bit in control register
    or  al, 1
    mov cr0, eax

    jmp ClearPreFetchQueue  ; Jump to the next instruction
ClearPreFetchQueue:         ; This flushes current realmode stuff

    ; Jump to the protected mode stuff
    jmp 0x08:ProtectedMode+KERNEL_OFF


;----------------------------------------------------------
EnableA20:
    cli
    xor cx, cx
.loop1:
    in  al, 0x64    ; Get input from keyboard status port
    test    al, 0x02    ; Test buffer full flag
    loopnz  .loop1

    mov al, 0xd1    ; Write to output port
    out 0x64, al
.loop2:             ; Wait until buffer is empty again
    in  al, 0x64
    test    al, 0x02
    loopnz  .loop2

    mov al, 0xdf    ; Set A20
    out 0x60, al

    mov cx, 0x14    ; This is approx a 25uS delay
.loop3:             ; that is needed to let the
    out 0xed, ax    ; kb controller execute our A20
    loop    .loop3      ; command...
    ret

;----------------------------------------------------------
; Detect CPU
DetectCPU:
    ; Check 8086
    pushf           ; save flags

    xor  ah, ah     ; Make high part of flags zero
    push    ax
    popf

    pushf           ; Check if the high bits are set
    pop ax
    cmp ah, 0xf0
    je  Noi386Found

    ; Check 80286
    mov ah, 0xf0    ; Set the high bits of the flag
    push    ax
    popf

    pushf           ; Are they cleared?
    pop ax
    and ah, 0xf0
    jz  Noi386Found

    popf
i386Found:
    ret

Noi386Found:
    mov si, msg_no386
    call    WriteMsg
    jmp $

    
; ------------------------------------------------
; write msg from ds:si on screen
WriteMsg:
    lodsb
    or  al, al
    jz  Done

    mov ah, 0x0e
    mov bx, 0x0007
    int 0x10
    jmp WriteMsg
Done:
    ret

; ------------------------------------------------

    msg_no386   db "Error: 386 or better not found.", 13, 10, 0

    GDT dw  GDT_SIZE        ; Some people place this structure
        dd  GDT_START       ; inside the NULL descriptor, I don't...

    GDT_START:
        ; Can't be used,  (0x00)
        null_desc   dw 0x0000   ; Limit Low
                dw 0x0000   ; Base Low
                db 0x00     ; Base Mid + Accessed
                db 0x00     ; Flagg stuff
                db 0x00     ; More flag + Limit High
                db 0x00     ; Base High
        ; 4gb flat code descriptor (0x08)
        code4gb_desc    dw 0x0000   ; Limit Low
                dw 0x0000   ; Base Low
                db 0x00     ; Base Mid + Accessed
                db 0x9A     ; Flagg stuff
                db 0xCF     ; More flag + Limit High
                db 0x00     ; Base High
        ; 4gb flat data descriptor (0x10)
        data4gb_desc    dw 0x0000   ; Limit Low
                dw 0x0000   ; Base Low
                db 0x00     ; Base Mid + Accessed
                db 0x93     ; Flagg stuff
                db 0xCF     ; More flag + Limit High
                db 0x00     ; Base High
        ; 4kb vga (0xb8000) descriptor (0x18)
        vga_desc    dw 0x1000   ; Limit Low
                dw 0x8000   ; Base Low
                db 0x0B     ; Base Mid + Accessed
                db 0x93     ; Flagg stuff
                db 0x8F     ; More flag + Limit High
                db 0x00     ; Base High
    GDT_SIZE EQU $-GDT_START


; ---------------------------------------------------------
;     FROM HERE ON: EVERYTHING IS IN PROTECTED MODE
; ---------------------------------------------------------
[bits 32]
ProtectedMode:
    mov ax, 0x10        ; Load 4GB data selector
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    call    headsize+KERNEL_ENTRY_POINT


; -------------------------------------------------

    KERNEL_OFF      EQU 0x500
    KERNEL_ENTRY_POINT  EQU 0x000000

    headsize    dw  $

Wat doet ie?

Als eerste weten we dat we op plaats 0x500 staan, da's namelijk waar de bootsector kernel.sys inlaad. We zetten dus eerst onze segmenten goed zodat we niet in de knoei komen met data. (de stack blijft wel nog op 0x9000:0xFFFF staan btw).

Hierna gaan we checken of we een 386 of hoger hebben. Dat is nodig omdat we anders helemaal niet eens naar protected mode kunnen schakelen (286 uitgezonderd, maar da's hell-on-earth). Nadat we een 386 hebben gevonden zetten we de A20 lijn op, zodat we onze complete geheugen fatsoenlijk kunnen aanspreken (lang leve de backwards compatibilty).

Btw, als je een os gaat bouwen die ECHT compatible wil zijn, kan ik je melden dan een OS zoals microsoft al 17 verschilende methodes heeft om de A20 lijn op te zetten, omdat dit soort dingen per computer NET een tikkie anders zijn. Wees dus niet te snel met roepen dat je OS compatible is omdat ie AL op 2 systemen vlekkeloos draait :)).

Goed, na de A20 lijn mag je eindelijk echte 386 code gaan gebruiken, en kunnen we ook de eax'jes, de movzx'jes en de lgdt'jes aanroepen. (probeer de bootsector zo 8086-compatible mogelijk te houden: een gebruiker ziet liever een melding op het scherm dat het OS niet kan draaien, dan dat het complete systeem hangt, omdat je toevallig een ongeldige instructie aanroept).

Wat we gaan doen, is de eerste GDT initaliseren. Deze GDT bestaat uit 4 descriptors:

* de null-descriptor (deze kan je niet aanspreken, maar moet wel aanwezig zijn).
* de 4gb code descriptor (deze descriptor laat je code runnen in het complete geheugenbereik).
* de 4gb data descriptor (deze descriptor laat je data lezen/schrijven in het complete geheugenbereik).
* de vga-descriptor (deze descriptor laat je data lezen/schrijven puur en alleen op het VGA-scherm).

Voor mensen met een ega-schermpje moeten ipv 0x8000 bij de vga-descriptor gewoon 0x0000 invullen. Zodoende wordt dit geheugen gemapped van 0xB8000 naar 0xB0000: het ega-video bereik.

Nadat de GDT geinitialiseerd is, kunnen we springen naar protected mode toe: eerst de PG-bit setten, en daarna een jump doen zodat de hele fetch-queue leeg word gezogen van de real-mode instructies (je hebt toch wel je intel-docs gelezen? :))

Hierna moeten we naar onze 32bits code springen, en dat doen we door te jumpen naar onze 4gb-code selector (da's 0x8) en als offset onze protected mode code.


Eindelijk in 386 code dus, waarna we de rest van onze segmenten (die nu selectoren heten) goed gaan zetten naar de 4gb data-descriptor (0x10). Dit is ook inclusief de stack, niet echt een ideale situatie, maar goed genoeg voor nu.

Nu zijn we klaar met alle initialisatie die plaats moest vinden voordat we naar de echte kernel kunnen gaan. Tijd om daar naartoe te springen.

Zoals je al weet, hebben we de C-kernel achter de head geplakt. Dus het begin van de kernel begint aan het einde van de head. Deze waarde zoeken we niet zelf uit, maar laten we de assembler zelf uitrekeken (zie de laatste regel in head.s). De C-kernel zelf kunnen we starten door gewoon vanaf de eerste byte te beginnen (waarom dit goed gaat hoor je hierna).




Als er nog geinteresseerden zijn: ik maak hierna nog eventjes een soort van mini-kernel die wat print op het beeldscherm in C, en daarna misschien wat globale info over wat een OS verder allemaal inhoud? (zoals memory managment, io, scheduling etc?)

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

Anoniem: 27915

Op donderdag 16 mei 2002 23:10 schreef JayTaph het volgende:
Als er nog geinteresseerden zijn: ik maak hierna nog eventjes een soort van mini-kernel die wat print op het beeldscherm in C, en daarna misschien wat globale info over wat een OS verder allemaal inhoud? (zoals memory managment, io, scheduling etc?)
Ik snap momenteel slechts de helft (d.w.z., ik snap het globale idee wel - maar ik ken nog steeds te weinig assembler om de code exact te snappen), maar als jij een kernel zover kunt krijgen om C code te kunnen executen, dan zou dat ongelofelijk gaaf zijn! :). Al is het maar dat ie in C printf() (printk()) kan doen. :P.

Acties:
  • 0 Henk 'm!

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 30-09-2023

JayTaph

Portability is for canoes.

Topicstarter
De Kernel in C
--------------

Moeilijkheidsgraad: makkelijk
Voorkennis: at&t style assembler + C
Referenties: Intel x86 Docs, gas docs, at&t assembly tutorials
Tools: gcc / djgpp

Nu is het OS aangekomen bij het uitvoeren van de C-kernel. Zoals je al gelezen had, laten we de kernel beginnen vanaf het begin van de file.
Waarom is dit nu zo moeilijk?

Een "normaal" C-programma wordt meestal gecompileerd geassembleerd en gelinkt naar een bepaald formaal (EXE, ELF etc). Deze formaten gaan er van uit dat ze op een bepaalde offset geladen (COM -filetjes starten op offset 0x100) etc. Zodra het programma is geladen, wordt er een stub uitgevoerd die normaal gesproken door de linker automatisch wordt meegelinkt aan je programma. Deze file heet meestal crt0. In deze file worden allemaal platform/OS technische dingen geregeld en uiteindelijk word de functie _main aangeroepen (dezelfde main() die je in je eigen C-programma schrijft).

Onze kernel wordt gewoon plain binary-formaat zodat er geen stubs aangehangen worden. Tijdens het linken van je kernel, zorg je er voor dat als eerste een aparte object word gelinkt: entry.o. In dit object staat maar 1 functie genaamd _start, die weer op zijn beurt kernel_entry() aanroept.

Als je dit niet op deze manier doet, dan weet je nevernooitniet op welke plek de linker de kernel_entry() functie neerzet, en kun je steeds je head.S blijven aanpassen. Nu weten we zeker dat offset 0 een functie bevat die dat allemaal voor ons kan regelen (de ideale situatie zou zijn om head.S te linken met de rest van de kernel. Op die manier kun je wel heel makkelijk uitvinden waar kernel_entry() zich bevind.
code:
1
2
3
4
5
void kernel_entry (void);       // External reference to the real kernel entrypoint

void _start (void) {            // Very heftige stub Copyright (C) 2002 JayTaph
    kernel_entry ();
}

Nu de werkelijke kernel. Omdat we nog eigen stdlib hebben, kunnen we nog geen gebruik maken van dingen zoals memcpy, strcpy, printf en consorten. Deze functies moeten we straks allemaal zelf gaan bouwen. Nu doen we gewoon simpel stringetjes afdrukken dmv vga_putchar().a
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
char data[] = "blaaaat";            // Zomaar wat data..

#define DESC_4GB_CODE       0x08
#define DESC_4GB_DATA       0x10
#define DESC_VGA        0x18

/*
 * Causes the system to "hang". Works for the time beeing..
 */
void deadlock (void) {
    for (;;) ;
}

/*
 * Plots a character onto the screen.
 */
void vga_put (int x, int y, char attr, char c) {
    short int value = attr * 256 + c;
    int offset = (y*160)+(x*2);
    
    __asm__ ("
        pushw   %%es
        movw    %%bx, %%es
        movw    %%ax, %%es:(%%edi)
        popw    %%es"
        :
        : "D" (offset), "b" (DESC_VGA), "a" (value)
    );
}

/*
 * Kernel entrypoint, our "void main(void)".
 */
void kernel_entry () {
    int i;

    for (i=0; i!=26; i++) vga_put (i, 10, 16+i, 'A'+i);
    for (i=0; testtekst[i]; i++) vga_put (i, 11, 31, testtekst[i]);
    for (i=26; i!=0; i--) vga_put (i, 12, 16+i, 'A'+i);
    deadlock ();
}

char testtekst[] = "Deze tekst zetten we zodadelijk op het scherm neer.. :)";

deze functie werkt op de volgende manier:
We krijgen mee: x en y coordiaat van de plek waarop we willen plotten, de attribuut (achtergrond*16+voorgrond), en het karakter. De functie rekent uit de offset uit en zorgt ervoor dat de attribuut+karakter worden samengevoegd.

Hierna krijgen we een stukje assembler dat het gaat plotten naar het vga-descriptor (in AT&T style). De rest van de functies zijn te basic om uit te leggen :+

Nou, de eerste echte kernel is klaar :). Eigenlijk moet je nog een fatsoenlijke stack definieeren, nieuwe kernel descriptors maken die zorgt dat de kernel per definitie niet buiten zichzelf kan lezen/schrijven/uitvoeren.

Binnenkort zal ik wel een simpel printf() routine schrijven, zodat je een beetje kunt klootvioolen (of misschien dat iemand anders die wil schrijven???)

Yo dawg, I heard you like posts so I posted below your post so you can post again.


Acties:
  • 0 Henk 'm!

  • DGTL_Magician
  • Registratie: Februari 2001
  • Laatst online: 11-06 12:27

DGTL_Magician

Kijkt regelmatig vooruit

Op maandag 20 mei 2002 21:50 schreef JayTaph het volgende:
De Kernel in C
--------------

Moeilijkheidsgraad: makkelijk
Voorkennis: at&t style assembler + C
Referenties: Intel x86 Docs, gas docs, at&t assembly tutorials
Tools: gcc / djgpp

Nu is het OS aangekomen bij het uitvoeren van de C-kernel. Zoals je al gelezen had, laten we de kernel beginnen vanaf het begin van de file.
Waarom is dit nu zo moeilijk?

Een "normaal" C-programma wordt meestal gecompileerd geassembleerd en gelinkt naar een bepaald formaal (EXE, ELF etc). Deze formaten gaan er van uit dat ze op een bepaalde offset geladen (COM -filetjes starten op offset 0x100) etc. Zodra het programma is geladen, wordt er een stub uitgevoerd die normaal gesproken door de linker automatisch wordt meegelinkt aan je programma. Deze file heet meestal crt0. In deze file worden allemaal platform/OS technische dingen geregeld en uiteindelijk word de functie _main aangeroepen (dezelfde main() die je in je eigen C-programma schrijft).

Onze kernel wordt gewoon plain binary-formaat zodat er geen stubs aangehangen worden. Tijdens het linken van je kernel, zorg je er voor dat als eerste een aparte object word gelinkt: entry.o. In dit object staat maar 1 functie genaamd _start, die weer op zijn beurt kernel_entry() aanroept.

Als je dit niet op deze manier doet, dan weet je nevernooitniet op welke plek de linker de kernel_entry() functie neerzet, en kun je steeds je head.S blijven aanpassen. Nu weten we zeker dat offset 0 een functie bevat die dat allemaal voor ons kan regelen (de ideale situatie zou zijn om head.S te linken met de rest van de kernel. Op die manier kun je wel heel makkelijk uitvinden waar kernel_entry() zich bevind.
code:
1
2
3
4
5
void kernel_entry (void);       // External reference to the real kernel entrypoint

void _start (void) {            // Very heftige stub Copyright (C) 2002 JayTaph
    kernel_entry ();
}

Nu de werkelijke kernel. Omdat we nog eigen stdlib hebben, kunnen we nog geen gebruik maken van dingen zoals memcpy, strcpy, printf en consorten. Deze functies moeten we straks allemaal zelf gaan bouwen. Nu doen we gewoon simpel stringetjes afdrukken dmv vga_putchar().a
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
char data[] = "blaaaat";            // Zomaar wat data..

#define DESC_4GB_CODE       0x08
#define DESC_4GB_DATA       0x10
#define DESC_VGA        0x18

/*
 * Causes the system to "hang". Works for the time beeing..
 */
void deadlock (void) {
    for (;;) ;
}

/*
 * Plots a character onto the screen.
 */
void vga_put (int x, int y, char attr, char c) {
    short int value = attr * 256 + c;
    int offset = (y*160)+(x*2);
    
    __asm__ ("
        pushw   %%es
        movw    %%bx, %%es
        movw    %%ax, %%es:(%%edi)
        popw    %%es"
        :
        : "D" (offset), "b" (DESC_VGA), "a" (value)
    );
}

/*
 * Kernel entrypoint, our "void main(void)".
 */
void kernel_entry () {
    int i;

    for (i=0; i!=26; i++) vga_put (i, 10, 16+i, 'A'+i);
    for (i=0; testtekst[i]; i++) vga_put (i, 11, 31, testtekst[i]);
    for (i=26; i!=0; i--) vga_put (i, 12, 16+i, 'A'+i);
    deadlock ();
}

char testtekst[] = "Deze tekst zetten we zodadelijk op het scherm neer.. :)";

deze functie werkt op de volgende manier:
We krijgen mee: x en y coordiaat van de plek waarop we willen plotten, de attribuut (achtergrond*16+voorgrond), en het karakter. De functie rekent uit de offset uit en zorgt ervoor dat de attribuut+karakter worden samengevoegd.

Hierna krijgen we een stukje assembler dat het gaat plotten naar het vga-descriptor (in AT&T style). De rest van de functies zijn te basic om uit te leggen :+

Nou, de eerste echte kernel is klaar :). Eigenlijk moet je nog een fatsoenlijke stack definieeren, nieuwe kernel descriptors maken die zorgt dat de kernel per definitie niet buiten zichzelf kan lezen/schrijven/uitvoeren.

Binnenkort zal ik wel een simpel printf() routine schrijven, zodat je een beetje kunt klootvioolen (of misschien dat iemand anders die wil schrijven???)
Ik hoop niet dat ik dit topic al te veel omhoog schop, maar ik was opzoek naar info over C kernels, en ik liep tegen jouw topic aan. Ik had al een bootloader geschreven met FAT12, maar nu wil ik een C kernel gaan gebruiken (ASM werkt perfect, maar ik wil een C kernel :P)
Om te testen pak ik jouw code en compileer het vervolgens zo:
code:
1
2
3
gcc -ffreestanding -o kernel1.o kernel1.c
gcc -ffreestanding -o kernel2.o kernel2.c
ld -Ttext 0x100000 --oformat binary -o kernel.bin kernel1.o kernel2.o

Vervolgens mik ik de kernel.bin op mijn floppy, druk hem in mijn 386 en zie: Kernel aan het starten tiktiktik. Klaar, en hij hangt. Er gebeurt dus helemaal niks.
Als ik een C kernel maak met daarin:
code:
1
2
3
void main(void) {

}

dan gaat ie lekker de bootloader weer opnieuw starten zoals het hoort. Ik gok dat het in mijn compile/linken zit, maar dit probleem heb ik dus met alle tutorials die ik gebruik.
Ik gebruik overigens Linux om te compilen.

Blog | aaZoo - (Wireless) Networking, Security, DDoS Mitigatie, Virtualisatie en Storage

Pagina: 1 2 Laatste