-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathindex.html
1844 lines (1758 loc) · 185 KB
/
index.html
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="pandoc" />
<meta name="author" content="Erik Helin, Adam Renberg" />
<title>The little book about OS development</title>
<style type="text/css">
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
margin: 0; padding: 0; vertical-align: baseline; border: none; }
table.sourceCode { width: 100%; line-height: 100%; }
td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
td.sourceCode { padding-left: 5px; }
code > span.kw { color: #007020; font-weight: bold; }
code > span.dt { color: #902000; }
code > span.dv { color: #40a070; }
code > span.bn { color: #40a070; }
code > span.fl { color: #40a070; }
code > span.ch { color: #4070a0; }
code > span.st { color: #4070a0; }
code > span.co { color: #60a0b0; font-style: italic; }
code > span.ot { color: #007020; }
code > span.al { color: #ff0000; font-weight: bold; }
code > span.fu { color: #06287e; }
code > span.er { color: #ff0000; font-weight: bold; }
</style>
<link rel="stylesheet" href="book.css" type="text/css" />
</head>
<body>
<div id='wrapper'>
<div id="header">
<h1 class="title">The little book about OS development</h1>
<h2 class="author">Erik Helin, Adam Renberg</h2>
</div>
<div class="subheader">
<a href="https://github.com/littleosbook/littleosbook/">2015-01-19 | Commit: fe83e27dab3c39930354d2dea83f6d4ee2928212</a>
<a class="pdflink" href="book.pdf" title="PDF version">PDF version</a>
</div>
<div id='content'>
<div id="TOC">
<h1>Contents</h1>
<ul>
<li><a href="#introduction"><span class="toc-section-number">1</span> Introduction</a><ul>
<li><a href="#about-the-book"><span class="toc-section-number">1.1</span> About the Book</a></li>
<li><a href="#the-reader"><span class="toc-section-number">1.2</span> The Reader</a></li>
<li><a href="#credits-thanks-and-acknowledgements"><span class="toc-section-number">1.3</span> Credits, Thanks and Acknowledgements</a></li>
<li><a href="#contributors"><span class="toc-section-number">1.4</span> Contributors</a></li>
<li><a href="#changes-and-corrections"><span class="toc-section-number">1.5</span> Changes and Corrections</a></li>
<li><a href="#issues-and-where-to-get-help"><span class="toc-section-number">1.6</span> Issues and where to get help</a></li>
<li><a href="#license"><span class="toc-section-number">1.7</span> License</a></li>
</ul></li>
<li><a href="#first-steps"><span class="toc-section-number">2</span> First Steps</a><ul>
<li><a href="#tools"><span class="toc-section-number">2.1</span> Tools</a><ul>
<li><a href="#quick-setup"><span class="toc-section-number">2.1.1</span> Quick Setup</a></li>
<li><a href="#programming-languages"><span class="toc-section-number">2.1.2</span> Programming Languages</a></li>
<li><a href="#host-operating-system"><span class="toc-section-number">2.1.3</span> Host Operating System</a></li>
<li><a href="#build-system"><span class="toc-section-number">2.1.4</span> Build System</a></li>
<li><a href="#virtual-machine"><span class="toc-section-number">2.1.5</span> Virtual Machine</a></li>
</ul></li>
<li><a href="#booting"><span class="toc-section-number">2.2</span> Booting</a><ul>
<li><a href="#bios"><span class="toc-section-number">2.2.1</span> BIOS</a></li>
<li><a href="#the-bootloader"><span class="toc-section-number">2.2.2</span> The Bootloader</a></li>
<li><a href="#the-operating-system"><span class="toc-section-number">2.2.3</span> The Operating System</a></li>
</ul></li>
<li><a href="#hello-cafebabe"><span class="toc-section-number">2.3</span> Hello Cafebabe</a><ul>
<li><a href="#compiling-the-operating-system"><span class="toc-section-number">2.3.1</span> Compiling the Operating System</a></li>
<li><a href="#linking-the-kernel"><span class="toc-section-number">2.3.2</span> Linking the Kernel</a></li>
<li><a href="#obtaining-grub"><span class="toc-section-number">2.3.3</span> Obtaining GRUB</a></li>
<li><a href="#building-an-iso-image"><span class="toc-section-number">2.3.4</span> Building an ISO Image</a></li>
<li><a href="#running-bochs"><span class="toc-section-number">2.3.5</span> Running Bochs</a></li>
</ul></li>
<li><a href="#further-reading"><span class="toc-section-number">2.4</span> Further Reading</a></li>
</ul></li>
<li><a href="#getting-to-c"><span class="toc-section-number">3</span> Getting to C</a><ul>
<li><a href="#setting-up-a-stack"><span class="toc-section-number">3.1</span> Setting Up a Stack</a></li>
<li><a href="#calling-c-code-from-assembly"><span class="toc-section-number">3.2</span> Calling C Code From Assembly</a><ul>
<li><a href="#packing-structs"><span class="toc-section-number">3.2.1</span> Packing Structs</a></li>
</ul></li>
<li><a href="#compiling-c-code"><span class="toc-section-number">3.3</span> Compiling C Code</a></li>
<li><a href="#build-tools"><span class="toc-section-number">3.4</span> Build Tools</a></li>
<li><a href="#further-reading-1"><span class="toc-section-number">3.5</span> Further Reading</a></li>
</ul></li>
<li><a href="#output"><span class="toc-section-number">4</span> Output</a><ul>
<li><a href="#interacting-with-the-hardware"><span class="toc-section-number">4.1</span> Interacting with the Hardware</a></li>
<li><a href="#the-framebuffer"><span class="toc-section-number">4.2</span> The Framebuffer</a><ul>
<li><a href="#writing-text"><span class="toc-section-number">4.2.1</span> Writing Text</a></li>
<li><a href="#moving-the-cursor"><span class="toc-section-number">4.2.2</span> Moving the Cursor</a></li>
<li><a href="#the-driver"><span class="toc-section-number">4.2.3</span> The Driver</a></li>
</ul></li>
<li><a href="#the-serial-ports"><span class="toc-section-number">4.3</span> The Serial Ports</a><ul>
<li><a href="#configuring-the-serial-port"><span class="toc-section-number">4.3.1</span> Configuring the Serial Port</a></li>
<li><a href="#configuring-the-line"><span class="toc-section-number">4.3.2</span> Configuring the Line</a></li>
<li><a href="#configuring-the-buffers"><span class="toc-section-number">4.3.3</span> Configuring the Buffers</a></li>
<li><a href="#configuring-the-modem"><span class="toc-section-number">4.3.4</span> Configuring the Modem</a></li>
<li><a href="#writing-data-to-the-serial-port"><span class="toc-section-number">4.3.5</span> Writing Data to the Serial Port</a></li>
<li><a href="#configuring-bochs"><span class="toc-section-number">4.3.6</span> Configuring Bochs</a></li>
<li><a href="#the-driver-1"><span class="toc-section-number">4.3.7</span> The Driver</a></li>
</ul></li>
<li><a href="#further-reading-2"><span class="toc-section-number">4.4</span> Further Reading</a></li>
</ul></li>
<li><a href="#segmentation"><span class="toc-section-number">5</span> Segmentation</a><ul>
<li><a href="#accessing-memory"><span class="toc-section-number">5.1</span> Accessing Memory</a></li>
<li><a href="#the-global-descriptor-table-gdt"><span class="toc-section-number">5.2</span> The Global Descriptor Table (GDT)</a></li>
<li><a href="#loading-the-gdt"><span class="toc-section-number">5.3</span> Loading the GDT</a></li>
<li><a href="#further-reading-3"><span class="toc-section-number">5.4</span> Further Reading</a></li>
</ul></li>
<li><a href="#interrupts-and-input"><span class="toc-section-number">6</span> Interrupts and Input</a><ul>
<li><a href="#interrupts-handlers"><span class="toc-section-number">6.1</span> Interrupts Handlers</a></li>
<li><a href="#creating-an-entry-in-the-idt"><span class="toc-section-number">6.2</span> Creating an Entry in the IDT</a></li>
<li><a href="#handling-an-interrupt"><span class="toc-section-number">6.3</span> Handling an Interrupt</a></li>
<li><a href="#creating-a-generic-interrupt-handler"><span class="toc-section-number">6.4</span> Creating a Generic Interrupt Handler</a></li>
<li><a href="#loading-the-idt"><span class="toc-section-number">6.5</span> Loading the IDT</a></li>
<li><a href="#programmable-interrupt-controller-pic"><span class="toc-section-number">6.6</span> Programmable Interrupt Controller (PIC)</a></li>
<li><a href="#reading-input-from-the-keyboard"><span class="toc-section-number">6.7</span> Reading Input from the Keyboard</a></li>
<li><a href="#further-reading-4"><span class="toc-section-number">6.8</span> Further Reading</a></li>
</ul></li>
<li><a href="#the-road-to-user-mode"><span class="toc-section-number">7</span> The Road to User Mode</a><ul>
<li><a href="#loading-an-external-program"><span class="toc-section-number">7.1</span> Loading an External Program</a><ul>
<li><a href="#grub-modules"><span class="toc-section-number">7.1.1</span> GRUB Modules</a></li>
</ul></li>
<li><a href="#executing-a-program"><span class="toc-section-number">7.2</span> Executing a Program</a><ul>
<li><a href="#a-very-simple-program"><span class="toc-section-number">7.2.1</span> A Very Simple Program</a></li>
<li><a href="#compiling"><span class="toc-section-number">7.2.2</span> Compiling</a></li>
<li><a href="#finding-the-program-in-memory"><span class="toc-section-number">7.2.3</span> Finding the Program in Memory</a></li>
<li><a href="#jumping-to-the-code"><span class="toc-section-number">7.2.4</span> Jumping to the Code</a></li>
</ul></li>
<li><a href="#the-beginning-of-user-mode"><span class="toc-section-number">7.3</span> The Beginning of User Mode</a></li>
</ul></li>
<li><a href="#a-short-introduction-to-virtual-memory"><span class="toc-section-number">8</span> A Short Introduction to Virtual Memory</a><ul>
<li><a href="#virtual-memory-through-segmentation"><span class="toc-section-number">8.1</span> Virtual Memory Through Segmentation?</a></li>
<li><a href="#further-reading-5"><span class="toc-section-number">8.2</span> Further Reading</a></li>
</ul></li>
<li><a href="#paging"><span class="toc-section-number">9</span> Paging</a><ul>
<li><a href="#why-paging"><span class="toc-section-number">9.1</span> Why Paging?</a></li>
<li><a href="#paging-in-x86"><span class="toc-section-number">9.2</span> Paging in x86</a><ul>
<li><a href="#identity-paging"><span class="toc-section-number">9.2.1</span> Identity Paging</a></li>
<li><a href="#enabling-paging"><span class="toc-section-number">9.2.2</span> Enabling Paging</a></li>
<li><a href="#a-few-details"><span class="toc-section-number">9.2.3</span> A Few Details</a></li>
</ul></li>
<li><a href="#paging-and-the-kernel"><span class="toc-section-number">9.3</span> Paging and the Kernel</a><ul>
<li><a href="#reasons-to-not-identity-map-the-kernel"><span class="toc-section-number">9.3.1</span> Reasons to Not Identity Map the Kernel</a></li>
<li><a href="#the-virtual-address-for-the-kernel"><span class="toc-section-number">9.3.2</span> The Virtual Address for the Kernel</a></li>
<li><a href="#placing-the-kernel-at-0xc0000000"><span class="toc-section-number">9.3.3</span> Placing the Kernel at <code>0xC0000000</code></a></li>
<li><a href="#higher-half-linker-script"><span class="toc-section-number">9.3.4</span> Higher-half Linker Script</a></li>
<li><a href="#entering-the-higher-half"><span class="toc-section-number">9.3.5</span> Entering the Higher Half</a></li>
<li><a href="#running-in-the-higher-half"><span class="toc-section-number">9.3.6</span> Running in the Higher Half</a></li>
</ul></li>
<li><a href="#virtual-memory-through-paging"><span class="toc-section-number">9.4</span> Virtual Memory Through Paging</a></li>
<li><a href="#further-reading-6"><span class="toc-section-number">9.5</span> Further Reading</a></li>
</ul></li>
<li><a href="#page-frame-allocation"><span class="toc-section-number">10</span> Page Frame Allocation</a><ul>
<li><a href="#managing-available-memory"><span class="toc-section-number">10.1</span> Managing Available Memory</a><ul>
<li><a href="#how-much-memory-is-there"><span class="toc-section-number">10.1.1</span> How Much Memory is There?</a></li>
<li><a href="#managing-available-memory-1"><span class="toc-section-number">10.1.2</span> Managing Available Memory</a></li>
</ul></li>
<li><a href="#how-can-we-access-a-page-frame"><span class="toc-section-number">10.2</span> How Can We Access a Page Frame?</a></li>
<li><a href="#a-kernel-heap"><span class="toc-section-number">10.3</span> A Kernel Heap</a></li>
<li><a href="#further-reading-7"><span class="toc-section-number">10.4</span> Further reading</a></li>
</ul></li>
<li><a href="#user-mode"><span class="toc-section-number">11</span> User Mode</a><ul>
<li><a href="#segments-for-user-mode"><span class="toc-section-number">11.1</span> Segments for User Mode</a></li>
<li><a href="#setting-up-for-user-mode"><span class="toc-section-number">11.2</span> Setting Up For User Mode</a></li>
<li><a href="#entering-user-mode"><span class="toc-section-number">11.3</span> Entering User Mode</a></li>
<li><a href="#using-c-for-user-mode-programs"><span class="toc-section-number">11.4</span> Using C for User Mode Programs</a><ul>
<li><a href="#a-c-library"><span class="toc-section-number">11.4.1</span> A C Library</a></li>
</ul></li>
<li><a href="#further-reading-8"><span class="toc-section-number">11.5</span> Further Reading</a></li>
</ul></li>
<li><a href="#file-systems"><span class="toc-section-number">12</span> File Systems</a><ul>
<li><a href="#why-a-file-system"><span class="toc-section-number">12.1</span> Why a File System?</a></li>
<li><a href="#a-simple-read-only-file-system"><span class="toc-section-number">12.2</span> A Simple Read-Only File System</a></li>
<li><a href="#inodes-and-writable-file-systems"><span class="toc-section-number">12.3</span> Inodes and Writable File Systems</a></li>
<li><a href="#a-virtual-file-system"><span class="toc-section-number">12.4</span> A Virtual File System</a></li>
<li><a href="#further-reading-9"><span class="toc-section-number">12.5</span> Further Reading</a></li>
</ul></li>
<li><a href="#system-calls"><span class="toc-section-number">13</span> System Calls</a><ul>
<li><a href="#designing-system-calls"><span class="toc-section-number">13.1</span> Designing System Calls</a></li>
<li><a href="#implementing-system-calls"><span class="toc-section-number">13.2</span> Implementing System Calls</a></li>
<li><a href="#further-reading-10"><span class="toc-section-number">13.3</span> Further Reading</a></li>
</ul></li>
<li><a href="#multitasking"><span class="toc-section-number">14</span> Multitasking</a><ul>
<li><a href="#creating-new-processes"><span class="toc-section-number">14.1</span> Creating New Processes</a></li>
<li><a href="#cooperative-scheduling-with-yielding"><span class="toc-section-number">14.2</span> Cooperative Scheduling with Yielding</a></li>
<li><a href="#preemptive-scheduling-with-interrupts"><span class="toc-section-number">14.3</span> Preemptive Scheduling with Interrupts</a><ul>
<li><a href="#programmable-interval-timer"><span class="toc-section-number">14.3.1</span> Programmable Interval Timer</a></li>
<li><a href="#separate-kernel-stacks-for-processes"><span class="toc-section-number">14.3.2</span> Separate Kernel Stacks for Processes</a></li>
<li><a href="#difficulties-with-preemptive-scheduling"><span class="toc-section-number">14.3.3</span> Difficulties with Preemptive Scheduling</a></li>
</ul></li>
<li><a href="#further-reading-11"><span class="toc-section-number">14.4</span> Further Reading</a></li>
</ul></li>
</ul>
</div>
<h1 id="introduction"><span class="header-section-number">1</span> Introduction</h1>
<p>This text is a practical guide to writing your own x86 operating system. It is designed to give enough help with the technical details while at the same time not reveal too much with samples and code excerpts. We’ve tried to collect parts of the vast (and often excellent) expanse of material and tutorials available, on the web and otherwise, and add our own insights into the problems we encountered and struggled with.</p>
<p>This book is not about the theory behind operating systems, or how any specific operating system (OS) works. For OS theory we recommend the book <em>Modern Operating Systems</em> by Andrew Tanenbaum <span class="citation">[1]</span>. Lists and details on current operating systems are available on the Internet.</p>
<p>The starting chapters are quite detailed and explicit, to quickly get you into coding. Later chapters give more of an outline of what is needed, as more and more of the implementation and design becomes up to the reader, who should now be more familiar with the world of kernel development. At the end of some chapters there are links for further reading, which might be interesting and give a deeper understanding of the topics covered.</p>
<p>In <a href="#first-steps">chapter 2</a> and <a href="#getting-to-c">3</a> we set up our development environment and boot up our OS kernel in a virtual machine, eventually starting to write code in C. We continue in <a href="#output">chapter 4</a> with writing to the screen and the serial port, and then we dive into segmentation in <a href="#segmentation">chapter 5</a> and interrupts and input in <a href="#interrupts-and-input">chapter 6</a>.</p>
<p>After this we have a quite functional but bare-bones OS kernel. In <a href="#the-road-to-user-mode">chapter 7</a> we start the road to user mode applications, with virtual memory through paging (<a href="#a-short-introduction-to-virtual-memory">chapter 8</a> and <a href="#paging">9</a>), memory allocation (<a href="#page-frame-allocation">chapter 10</a>), and finally running a user application in <a href="#user-mode">chapter 11</a>.</p>
<p>In the last three chapters we discuss the more advanced topics of file systems (<a href="#file-systems">chapter 12</a>), system calls (<a href="#system-calls">chapter 13</a>), and multitasking (<a href="#multitasking">chapter 14</a>).</p>
<h2 id="about-the-book"><span class="header-section-number">1.1</span> About the Book</h2>
<p>The OS kernel and this book were produced as part of an advanced individual course at the Royal Institute of Technology <span class="citation">[2]</span>, Stockholm. The authors had previously taken courses in OS theory, but had only minor practical experience with OS kernel development. In order to get more insight and a deeper understanding of how the theory from the previous OS courses works out in practice, the authors decided to create a new course, which focused on the development of a small OS. Another goal of the course was writing a thorough tutorial on how to develop a small OS basically from scratch, and this short book is the result.</p>
<p>The x86 architecture is, and has been for a long time, one of the most common hardware architectures. It was not a difficult choice to use the x86 architecture as the target of the OS, with its large community, extensive reference material and mature emulators. The documentation and information surrounding the details of the hardware we had to work with was not always easy to find or understand, despite (or perhaps due to) the age of the architecture.</p>
<p>The OS was developed in about six weeks of full-time work. The implementation was done in many small steps, and after each step the OS was tested manually. By developing in this incremental and iterative way, it was often easier to find any bugs that were introduced, since only a small part of the code had changed since the last known good state of the code. We encourage the reader to work in a similar way.</p>
<p>During the six weeks of development, almost every single line of code was written by the authors together (this way of working is also called <em>pair-programming</em>). It is our belief that we managed to avoid a lot of bugs due to this style of development, but this is hard to prove scientifically.</p>
<h2 id="the-reader"><span class="header-section-number">1.2</span> The Reader</h2>
<p>The reader of this book should be comfortable with UNIX/Linux, systems programming, the C language and computer systems in general (such as hexadecimal notation <span class="citation">[3]</span>). This book could be a way to get started learning those things, but it will be more difficult, and developing an operating system is already challenging on its own. Search engines and other tutorials are often helpful if you get stuck.</p>
<h2 id="credits-thanks-and-acknowledgements"><span class="header-section-number">1.3</span> Credits, Thanks and Acknowledgements</h2>
<p>We’d like to thank the OSDev community <span class="citation">[4]</span> for their great wiki and helpful members, and James Malloy for his eminent kernel development tutorial <span class="citation">[5]</span>. We’d also like to thank our supervisor Torbjörn Granlund for his insightful questions and interesting discussions.</p>
<p>Most of the CSS formatting of the book is based on the work by Scott Chacon for the book Pro Git, <a href="http://progit.org/">http://progit.org/</a>.</p>
<h2 id="contributors"><span class="header-section-number">1.4</span> Contributors</h2>
<p>We are very grateful for the patches that people send us. The following users have all contributed to this book:</p>
<ul>
<li><a href="https://github.com/alexschneider">alexschneider</a></li>
<li><a href="https://github.com/Avidanborisov">Avidanborisov</a></li>
<li><a href="https://github.com/nirs">nirs</a></li>
<li><a href="https://github.com/kedarmhaswade">kedarmhaswade</a></li>
<li><a href="https://github.com/vamanea">vamanea</a></li>
<li><a href="https://github.com/ansjob">ansjob</a></li>
</ul>
<h2 id="changes-and-corrections"><span class="header-section-number">1.5</span> Changes and Corrections</h2>
<p>This book is hosted on Github - if you have any suggestions, comments or corrections, just fork the book, write your changes, and send us a pull request. We’ll happily incorporate anything that makes this book better.</p>
<h2 id="issues-and-where-to-get-help"><span class="header-section-number">1.6</span> Issues and where to get help</h2>
<p>If you run into problems while reading the book, please check the issues on Github for help: <a href="https://github.com/littleosbook/littleosbook/issues">https://github.com/littleosbook/littleosbook/issues</a>.</p>
<h2 id="license"><span class="header-section-number">1.7</span> License</h2>
<p>All content is under the Creative Commons Attribution Non Commercial Share Alike 3.0 license, <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/us/">http://creativecommons.org/licenses/by-nc-sa/3.0/us/</a>. The code samples are in the public domain - use them however you want. References to this book are always received with warmth.</p>
<h1 id="first-steps"><span class="header-section-number">2</span> First Steps</h1>
<p>Developing an operating system (OS) is no easy task, and the question “How do I even begin to solve this problem?” is likely to come up several times during the course of the project for different problems. This chapter will help you set up your development environment and booting a very small (and primitive) operating system.</p>
<h2 id="tools"><span class="header-section-number">2.1</span> Tools</h2>
<h3 id="quick-setup"><span class="header-section-number">2.1.1</span> Quick Setup</h3>
<p>We (the authors) have used Ubuntu <span class="citation">[6]</span> as the operating system for doing OS development, running it both physically and virtually (using the virtual machine VirtualBox <span class="citation">[7]</span>). A quick way to get everything up and running is to use the same setup as we did, since we know that these tools work with the samples provided in this book.</p>
<p>Once Ubuntu is installed, either physical or virtual, the following packages should be installed using <code>apt-get</code>:</p>
<pre class="sourceCode bash"><code class="sourceCode bash"> <span class="kw">sudo</span> apt-get install build-essential nasm genisoimage bochs bochs-sdl</code></pre>
<h3 id="programming-languages"><span class="header-section-number">2.1.2</span> Programming Languages</h3>
<p>The operating system will be developed using the C programming language <span class="citation">[8]</span><span class="citation">[9]</span>, using GCC <span class="citation">[10]</span>. We use C because developing an OS requires a very precise control of the generated code and direct access to memory. Other languages that provide the same features can also be used, but this book will only cover C.</p>
<p>The code will make use of one type attribute that is specific for GCC:</p>
<pre><code> __attribute__((packed))</code></pre>
<p>This attribute allows us to ensure that the compiler uses a memory layout for a <code>struct</code> exactly as we define it in the code. This is explained in more detail in the next chapter.</p>
<p>Due to this attribute, the example code might be hard to compile using a C compiler other than GCC.</p>
<p>For writing assembly code, we have chosen NASM <span class="citation">[11]</span> as the assembler, since we prefer NASM’s syntax over GNU Assembler.</p>
<p>Bash <span class="citation">[12]</span> will be used as the scripting language throughout the book.</p>
<h3 id="host-operating-system"><span class="header-section-number">2.1.3</span> Host Operating System</h3>
<p>All the code examples assumes that the code is being compiled on a UNIX like operating system. All code examples have been successfully compiled using Ubuntu <span class="citation">[6]</span> versions 11.04 and 11.10.</p>
<h3 id="build-system"><span class="header-section-number">2.1.4</span> Build System</h3>
<p>Make <span class="citation">[13]</span> has been used when constructing the Makefile examples.</p>
<h3 id="virtual-machine"><span class="header-section-number">2.1.5</span> Virtual Machine</h3>
<p>When developing an OS it is very convenient to be able to run your code in a <em>virtual machine</em> instead of on a physical computer, since starting your OS in a virtual machine is much faster than getting your OS onto a physical medium and then running it on a physical machine. Bochs <span class="citation">[14]</span> is an emulator for the x86 (IA-32) platform which is well suited for OS development due to its debugging features. Other popular choices are QEMU <span class="citation">[15]</span> and VirtualBox <span class="citation">[7]</span>. This book uses Bochs.</p>
<p>By using a virtual machine we cannot ensure that our OS works on real, physical hardware. The environment simulated by the virtual machine is designed to be very similar to their physical counterparts, and the OS can be tested on one by just copying the executable to a CD and finding a suitable machine.</p>
<h2 id="booting"><span class="header-section-number">2.2</span> Booting</h2>
<p>Booting an operating system consists of transferring control along a chain of small programs, each one more “powerful” than the previous one, where the operating system is the last “program”. See the following figure for an example of the boot process:</p>
<div class="figure">
<img src="images/boot_chain.png" alt="An example of the boot process. Each box is a program." /><p class="caption">An example of the boot process. Each box is a program.</p>
</div>
<h3 id="bios"><span class="header-section-number">2.2.1</span> BIOS</h3>
<p>When the PC is turned on, the computer will start a small program that adheres to the <em>Basic Input Output System</em> (BIOS) <span class="citation">[16]</span> standard. This program is usually stored on a read only memory chip on the motherboard of the PC. The original role of the BIOS program was to export some library functions for printing to the screen, reading keyboard input etc. Modern operating systems do not use the BIOS’ functions, they use drivers that interact directly with the hardware, bypassing the BIOS. Today, BIOS mainly runs some early diagnostics (power-on-self-test) and then transfers control to the bootloader.</p>
<h3 id="the-bootloader"><span class="header-section-number">2.2.2</span> The Bootloader</h3>
<p>The BIOS program will transfer control of the PC to a program called a <em>bootloader</em>. The bootloader’s task is to transfer control to us, the operating system developers, and our code. However, due to some restrictions<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a> of the hardware and because of backward compatibility, the bootloader is often split into two parts: the first part of the bootloader will transfer control to the second part, which finally gives control of the PC to the operating system.</p>
<p>Writing a bootloader involves writing a lot of low-level code that interacts with the BIOS. Therefore, an existing bootloader will be used: the GNU GRand Unified Bootloader (GRUB) <span class="citation">[17]</span>.</p>
<p>Using GRUB, the operating system can be built as an ordinary ELF <span class="citation">[18]</span> executable, which will be loaded by GRUB into the correct memory location. The compilation of the kernel requires that the code is laid out in memory in a specific way (how to compile the kernel will be discussed later in this chapter).</p>
<h3 id="the-operating-system"><span class="header-section-number">2.2.3</span> The Operating System</h3>
<p>GRUB will transfer control to the operating system by jumping to a position in memory. Before the jump, GRUB will look for a magic number to ensure that it is actually jumping to an OS and not some random code. This magic number is part of the <em>multiboot specification</em> <span class="citation">[19]</span> which GRUB adheres to. Once GRUB has made the jump, the OS has full control of the computer.</p>
<h2 id="hello-cafebabe"><span class="header-section-number">2.3</span> Hello Cafebabe</h2>
<p>This section will describe how to implement of the smallest possible OS that can be used together with GRUB. The only thing the OS will do is write <code>0xCAFEBABE</code> to the <code>eax</code> register (most people would probably not even call this an OS).</p>
<h3 id="compiling-the-operating-system"><span class="header-section-number">2.3.1</span> Compiling the Operating System</h3>
<p>This part of the OS has to be written in assembly code, since C requires a stack, which isn’t available (the chapter <a href="#getting-to-c">“Getting to C”</a> describes how to set one up). Save the following code in a file called <code>loader.s</code>:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> <span class="kw">global</span> loader <span class="co">; the entry symbol for ELF</span>
MAGIC_NUMBER <span class="dt">equ</span> <span class="bn">0x1BADB002</span> <span class="co">; define the magic number constant</span>
FLAGS <span class="dt">equ</span><span class="bn"> 0x0 </span><span class="co">; multiboot flags</span>
CHECKSUM <span class="dt">equ</span> -MAGIC_NUMBER <span class="co">; calculate the checksum</span>
<span class="co">; (magic number + checksum + flags should equal 0)</span>
<span class="kw">section</span> .text: <span class="co">; start of the text (code) section</span>
<span class="kw">align</span> <span class="dv">4</span> <span class="co">; the code must be 4 byte aligned</span>
<span class="dt">dd</span> MAGIC_NUMBER <span class="co">; write the magic number to the machine code,</span>
<span class="dt">dd</span> FLAGS <span class="co">; the flags,</span>
<span class="dt">dd</span> CHECKSUM <span class="co">; and the checksum</span>
<span class="fu"> loader:</span> <span class="co">; the loader label (defined as entry point in linker script)</span>
<span class="kw">mov</span> <span class="kw">eax</span>, <span class="bn">0xCAFEBABE</span> <span class="co">; place the number 0xCAFEBABE in the register eax</span>
<span class="fu"> .loop:</span>
<span class="kw">jmp</span> .<span class="kw">loop</span> <span class="co">; loop forever</span></code></pre>
<p>The only thing this OS will do is write the very specific number <code>0xCAFEBABE</code> to the <code>eax</code> register. It is <em>very</em> unlikely that the number <code>0xCAFEBABE</code> would be in the <code>eax</code> register if the OS did <em>not</em> put it there.</p>
<p>The file <code>loader.s</code> can be compiled into a 32 bits ELF <span class="citation">[18]</span> object file with the following command:</p>
<pre class="sourceCode bash"><code class="sourceCode bash"> <span class="kw">nasm</span> -f elf32 loader.s</code></pre>
<h3 id="linking-the-kernel"><span class="header-section-number">2.3.2</span> Linking the Kernel</h3>
<p>The code must now be linked to produce an executable file, which requires some extra thought compared to when linking most programs. We want GRUB to load the kernel at a memory address larger than or equal to <code>0x00100000</code> (1 megabyte (MB)), because addresses lower than 1 MB are used by GRUB itself, BIOS and memory-mapped I/O. Therefore, the following linker script is needed (written for GNU LD <span class="citation">[20]</span>):</p>
<pre><code>ENTRY(loader) /* the name of the entry label */
SECTIONS {
. = 0x00100000; /* the code should be loaded at 1 MB */
.text ALIGN (0x1000) : /* align at 4 KB */
{
*(.text) /* all text sections from all files */
}
.rodata ALIGN (0x1000) : /* align at 4 KB */
{
*(.rodata*) /* all read-only data sections from all files */
}
.data ALIGN (0x1000) : /* align at 4 KB */
{
*(.data) /* all data sections from all files */
}
.bss ALIGN (0x1000) : /* align at 4 KB */
{
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all bss sections from all files */
}
}</code></pre>
<p>Save the linker script into a file called <code>link.ld</code>. The executable can now be linked with the following command:</p>
<pre class="sourceCode bash"><code class="sourceCode bash"> <span class="kw">ld</span> -T link.ld -melf_i386 loader.o -o kernel.elf</code></pre>
<p>The final executable will be called <code>kernel.elf</code>.</p>
<h3 id="obtaining-grub"><span class="header-section-number">2.3.3</span> Obtaining GRUB</h3>
<p>The GRUB version we will use is GRUB Legacy, since the OS ISO image can then be generated on systems using both GRUB Legacy and GRUB 2. More specifically, the GRUB Legacy <code>stage2_eltorito</code> bootloader will be used. This file can be built from GRUB 0.97 by downloading the source from <a href="ftp://alpha.gnu.org/gnu/grub/grub-0.97.tar.gz">ftp://alpha.gnu.org/gnu/grub/grub-0.97.tar.gz</a>. However, the <code>configure</code> script doesn’t work well with Ubuntu <span class="citation">[21]</span>, so the binary file can be downloaded from <a href="http://littleosbook.github.com/files/stage2_eltorito">http://littleosbook.github.com/files/stage2_eltorito</a>. Copy the file <code>stage2_eltorito</code> to the folder that already contains <code>loader.s</code> and <code>link.ld</code>.</p>
<h3 id="building-an-iso-image"><span class="header-section-number">2.3.4</span> Building an ISO Image</h3>
<p>The executable must be placed on a media that can be loaded by a virtual or physical machine. In this book we will use ISO <span class="citation">[22]</span> image files as the media, but one can also use floppy images, depending on what the virtual or physical machine supports.</p>
<p>We will create the kernel ISO image with the program <code>genisoimage</code>. A folder must first be created that contains the files that will be on the ISO image. The following commands create the folder and copy the files to their correct places:</p>
<pre class="sourceCode bash"><code class="sourceCode bash"> <span class="kw">mkdir</span> -p iso/boot/grub <span class="co"># create the folder structure</span>
<span class="kw">cp</span> stage2_eltorito iso/boot/grub/ <span class="co"># copy the bootloader</span>
<span class="kw">cp</span> kernel.elf iso/boot/ <span class="co"># copy the kernel</span></code></pre>
<p>A configuration file <code>menu.lst</code> for GRUB must be created. This file tells GRUB where the kernel is located and configures some options:</p>
<pre><code> default=0
timeout=0
title os
kernel /boot/kernel.elf</code></pre>
<p>Place the file <code>menu.lst</code> in the folder <code>iso/boot/grub/</code>. The contents of the <code>iso</code> folder should now look like the following figure:</p>
<pre><code> iso
|-- boot
|-- grub
| |-- menu.lst
| |-- stage2_eltorito
|-- kernel.elf</code></pre>
<p>The ISO image can then be generated with the following command:</p>
<pre><code> genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-boot-load-size 4 \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso</code></pre>
<p>For more information about the flags used in the command, see the manual for <code>genisoimage</code>.</p>
<p>The ISO image <code>os.iso</code> now contains the kernel executable, the GRUB bootloader and the configuration file.</p>
<h3 id="running-bochs"><span class="header-section-number">2.3.5</span> Running Bochs</h3>
<p>Now we can run the OS in the Bochs emulator using the <code>os.iso</code> ISO image. Bochs needs a configuration file to start and an example of a simple configuration file is given below:</p>
<pre><code> megs: 32
display_library: sdl
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
ata0-master: type=cdrom, path=os.iso, status=inserted
boot: cdrom
log: bochslog.txt
clock: sync=realtime, time0=local
cpu: count=1, ips=1000000</code></pre>
<p>You might need to change the path to <code>romimage</code> and <code>vgaromimage</code> depending on how you installed Bochs. More information about the Bochs config file can be found at Boch’s website <span class="citation">[23]</span>.</p>
<p>If you saved the configuration in a file named <code>bochsrc.txt</code> then you can run Bochs with the following command:</p>
<pre><code> bochs -f bochsrc.txt -q</code></pre>
<p>The flag <code>-f</code> tells Bochs to use the given configuration file and the flag <code>-q</code> tells Bochs to skip the interactive start menu. You should now see Bochs starting and displaying a console with some information from GRUB on it.</p>
<p>After quitting Bochs, display the log produced by Boch:</p>
<pre><code> cat bochslog.txt</code></pre>
<p>You should now see the contents of the registers of the CPU simulated by Bochs somewhere in the output. If you find <code>RAX=00000000CAFEBABE</code> or <code>EAX=CAFEBABE</code> (depending on if you are running Bochs with or without 64 bit support) in the output then your OS has successfully booted!</p>
<h2 id="further-reading"><span class="header-section-number">2.4</span> Further Reading</h2>
<ul>
<li>Gustavo Duertes has written an in-depth article about what actually happens when a x86 computer boots up, <a href="http://duartes.org/gustavo/blog/post/how-computers-boot-up">http://duartes.org/gustavo/blog/post/how-computers-boot-up</a></li>
<li>Gustavo continues to describe what the kernel does in the very early stages at <a href="http://duartes.org/gustavo/blog/post/kernel-boot-process">http://duartes.org/gustavo/blog/post/kernel-boot-process</a></li>
<li>The OSDev wiki also contains a nice article about booting an x86 computer: <a href="http://wiki.osdev.org/Boot_Sequence">http://wiki.osdev.org/Boot_Sequence</a></li>
</ul>
<h1 id="getting-to-c"><span class="header-section-number">3</span> Getting to C</h1>
<p>This chapter will show you how to use C instead of assembly code as the programming language for the OS. Assembly is very good for interacting with the CPU and enables maximum control over every aspect of the code. However, at least for the authors, C is a much more convenient language to use. Therefore, we would like to use C as much as possible and use assembly code only where it make sense.</p>
<h2 id="setting-up-a-stack"><span class="header-section-number">3.1</span> Setting Up a Stack</h2>
<p>One prerequisite for using C is a stack, since all non-trivial C programs use a stack. Setting up a stack is not harder than to make the <code>esp</code> register point to the end of an area of free memory (remember that the stack grows towards lower addresses on the x86) that is correctly aligned (alignment on 4 bytes is recommended from a performance perspective).</p>
<p>We could point <code>esp</code> to a random area in memory since, so far, the only thing in the memory is GRUB, BIOS, the OS kernel and some memory-mapped I/O. This is not a good idea - we don’t know how much memory is available or if the area <code>esp</code> would point to is used by something else. A better idea is to reserve a piece of uninitialized memory in the <code>bss</code> section in the ELF file of the kernel. It is better to use the <code>bss</code> section instead of the <code>data</code> section to reduce the size of the OS executable. Since GRUB understands ELF, GRUB will allocate any memory reserved in the <code>bss</code> section when loading the OS.</p>
<p>The NASM pseudo-instruction <code>resb</code> <span class="citation">[24]</span> can be used to declare uninitialized data:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> KERNEL_STACK_SIZE <span class="dt">equ</span> <span class="dv">4096</span> <span class="co">; size of stack in bytes</span>
<span class="kw">section</span> .bss
<span class="kw">align</span> <span class="dv">4</span> <span class="co">; align at 4 bytes</span>
<span class="fu"> kernel_stack:</span> <span class="co">; label points to beginning of memory</span>
<span class="dt">resb</span> KERNEL_STACK_SIZE <span class="co">; reserve stack for the kernel</span></code></pre>
<p>There is no need to worry about the use of uninitialized memory for the stack, since it is not possible to read a stack location that has not been written (without manual pointer fiddling). A (correct) program can not pop an element from the stack without having pushed an element onto the stack first. Therefore, the memory locations of the stack will always be written to before they are being read.</p>
<p>The stack pointer is then set up by pointing <code>esp</code> to the end of the <code>kernel_stack</code> memory:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> <span class="kw">mov</span> <span class="kw">esp</span>, kernel_stack + KERNEL_STACK_SIZE <span class="co">; point esp to the start of the</span>
<span class="co">; stack (end of memory area)</span></code></pre>
<h2 id="calling-c-code-from-assembly"><span class="header-section-number">3.2</span> Calling C Code From Assembly</h2>
<p>The next step is to call a C function from assembly code. There are many different conventions for how to call C code from assembly code <span class="citation">[25]</span>. This book uses the <em>cdecl</em> calling convention, since that is the one used by GCC. The cdecl calling convention states that arguments to a function should be passed via the stack (on x86). The arguments of the function should be pushed on the stack in a right-to-left order, that is, you push the rightmost argument first. The return value of the function is placed in the <code>eax</code> register. The following code shows an example:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="co">/* The C function */</span>
<span class="dt">int</span> sum_of_three(<span class="dt">int</span> arg1, <span class="dt">int</span> arg2, <span class="dt">int</span> arg3)
{
<span class="kw">return</span> arg1 + arg2 + arg3;
}</code></pre>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> <span class="co">; The assembly code</span>
external sum_of_three <span class="co">; the function sum_of_three is defined elsewhere</span>
<span class="kw">push</span> <span class="dt">dword</span> <span class="dv">3</span> <span class="co">; arg3</span>
<span class="kw">push</span> <span class="dt">dword</span> <span class="dv">2</span> <span class="co">; arg2</span>
<span class="kw">push</span> <span class="dt">dword</span> <span class="dv">1</span> <span class="co">; arg1</span>
<span class="kw">call</span> sum_of_three <span class="co">; call the function, the result will be in eax</span></code></pre>
<h3 id="packing-structs"><span class="header-section-number">3.2.1</span> Packing Structs</h3>
<p>In the rest of this book, you will often come across “configuration bytes” that are a collection of bits in a very specific order. Below follows an example with 32 bits:</p>
<pre><code>Bit: | 31 24 | 23 8 | 7 0 |
Content: | index | address | config |</code></pre>
<p>Instead of using an unsigned integer, <code>unsigned int</code>, for handling such configurations, it is much more convenient to use “packed structures”:</p>
<pre class="sourceCode C"><code class="sourceCode c"> <span class="kw">struct</span> example {
<span class="dt">unsigned</span> <span class="dt">char</span> config; <span class="co">/* bit 0 - 7 */</span>
<span class="dt">unsigned</span> <span class="dt">short</span> address; <span class="co">/* bit 8 - 23 */</span>
<span class="dt">unsigned</span> <span class="dt">char</span> index; <span class="co">/* bit 24 - 31 */</span>
};</code></pre>
<p>When using the <code>struct</code> in the previous example there is no guarantee that the size of the <code>struct</code> will be exactly 32 bits - the compiler can add some padding between elements for various reasons, for example to speed up element access or due to requirements set by the hardware and/or compiler. When using a <code>struct</code> to represent configuration bytes, it is very important that the compiler does <em>not</em> add any padding, because the <code>struct</code> will eventually be treated as a 32 bit unsigned integer by the hardware. The attribute <code>packed</code> can be used to force GCC to <em>not</em> add any padding:</p>
<pre class="sourceCode C"><code class="sourceCode c"> <span class="kw">struct</span> example {
<span class="dt">unsigned</span> <span class="dt">char</span> config; <span class="co">/* bit 0 - 7 */</span>
<span class="dt">unsigned</span> <span class="dt">short</span> address; <span class="co">/* bit 8 - 23 */</span>
<span class="dt">unsigned</span> <span class="dt">char</span> index; <span class="co">/* bit 24 - 31 */</span>
} __attribute__((packed));</code></pre>
<p>Note that <code>__attribute__((packed))</code> is not part of the C standard - it might not work with all C compilers.</p>
<h2 id="compiling-c-code"><span class="header-section-number">3.3</span> Compiling C Code</h2>
<p>When compiling the C code for the OS, a lot of flags to GCC need to be used. This is because the C code should <em>not</em> assume the presence of a standard library, since there is no standard library available for our OS. For more information about the flags, see the GCC manual.</p>
<p>The flags used for compiling the C code are:</p>
<pre><code> -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles
-nodefaultlibs</code></pre>
<p>As always when writing C programs we recommend turning on all warnings and treat warnings as errors:</p>
<pre><code> -Wall -Wextra -Werror</code></pre>
<p>You can now create a function <code>kmain</code> in a file called <code>kmain.c</code> that you call from <code>loader.s</code>. At this point, <code>kmain</code> probably won’t need any arguments (but in later chapters it will).</p>
<h2 id="build-tools"><span class="header-section-number">3.4</span> Build Tools</h2>
<p>Now is also probably a good time to set up some build tools to make it easier to compile and test-run the OS. We recommend using <code>make</code> <span class="citation">[13]</span>, but there are plenty of other build systems available. A simple Makefile for the OS could look like the following example:</p>
<pre class="sourceCode Makefile"><code class="sourceCode makefile"> <span class="dt">OBJECTS </span><span class="ch">=</span><span class="st"> loader.o kmain.o</span>
<span class="dt">CC </span><span class="ch">=</span><span class="st"> gcc</span>
<span class="dt">CFLAGS </span><span class="ch">=</span><span class="st"> -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector </span><span class="ch">\</span>
<span class="ch">-</span><span class="fu">nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c</span>
<span class="dt">LDFLAGS </span><span class="ch">=</span><span class="st"> -T link.ld -melf_i386</span>
<span class="dt">AS </span><span class="ch">=</span><span class="st"> nasm</span>
<span class="dt">ASFLAGS </span><span class="ch">=</span><span class="st"> -f elf</span>
all: kernel.elf
kernel.elf: <span class="ch">$(</span><span class="dt">OBJECTS</span><span class="ch">)</span>
ld <span class="ch">$(</span><span class="dt">LDFLAGS</span><span class="ch">)</span> <span class="ch">$(</span><span class="dt">OBJECTS</span><span class="ch">)</span> -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
<span class="ch">-</span><span class="fu">b boot/grub/stage2_eltorito </span><span class="ch">\</span>
<span class="ch">-</span><span class="fu">no-emul-boot </span><span class="ch">\</span>
<span class="ch">-</span><span class="fu">boot-load-size 4 </span><span class="ch">\</span>
<span class="ch">-</span><span class="fu">A os </span><span class="ch">\</span>
<span class="ch">-</span><span class="fu">input-charset utf8 </span><span class="ch">\</span>
<span class="ch">-</span><span class="fu">quiet </span><span class="ch">\</span>
<span class="ch">-</span><span class="fu">boot-info-table </span><span class="ch">\</span>
<span class="ch">-</span><span class="fu">o os.iso </span><span class="ch">\</span>
iso
run: os.iso
bochs -f bochsrc.txt -q
%.o: %.c
<span class="ch">$(</span><span class="dt">CC</span><span class="ch">)</span> <span class="ch">$(</span><span class="dt">CFLAGS</span><span class="ch">)</span> <span class="ch">$<</span> -o <span class="ch">$@</span>
%.o: %.s
<span class="ch">$(</span><span class="dt">AS</span><span class="ch">)</span> <span class="ch">$(</span><span class="dt">ASFLAGS</span><span class="ch">)</span> <span class="ch">$<</span> -o <span class="ch">$@</span>
clean:
rm -rf *.o kernel.elf os.iso</code></pre>
<p>The contents of your working directory should now look like the following figure:</p>
<pre><code> .
|-- bochsrc.txt
|-- iso
| |-- boot
| |-- grub
| |-- menu.lst
| |-- stage2_eltorito
|-- kmain.c
|-- loader.s
|-- Makefile</code></pre>
<p>You should now be able to start the OS with the simple command <code>make run</code>, which will compile the kernel and boot it up in Bochs (as defined in the Makefile above).</p>
<h2 id="further-reading-1"><span class="header-section-number">3.5</span> Further Reading</h2>
<ul>
<li>Kernigan & Richie’s book, <em>The C Programming Language, Second Edition</em>, <span class="citation">[8]</span> is great for learning about all the aspects of C.</li>
</ul>
<h1 id="output"><span class="header-section-number">4</span> Output</h1>
<p>This chapter will present how to display text on the console as well as writing data to the serial port. Furthermore, we will create our first <em>driver</em>, that is, code that acts as a layer between the kernel and the hardware, providing a higher abstraction than communicating directly with the hardware. The first part of this chapter is about creating a driver for the <em>framebuffer</em> <span class="citation">[26]</span> to be able to display text on the console. The second part shows how to create a driver for the serial port. Bochs can store output from the serial port in a file, effectively creating a logging mechanism for the operating system.</p>
<h2 id="interacting-with-the-hardware"><span class="header-section-number">4.1</span> Interacting with the Hardware</h2>
<p>There are usually two different ways to interact with the hardware, <em>memory-mapped I/O</em> and <em>I/O ports</em>.</p>
<p>If the hardware uses memory-mapped I/O then you can write to a specific memory address and the hardware will be updated with the new data. One example of this is the framebuffer, which will be discussed in more detail later. For example, if you write the value <code>0x410F</code> to address <code>0x000B8000</code>, you will see the letter A in white color on a black background (see the section on <a href="#the-framebuffer">the framebuffer</a> for more details).</p>
<p>If the hardware uses I/O ports then the assembly code instructions <code>out</code> and <code>in</code> must be used to communicate with the hardware. The instruction <code>out</code> takes two parameters: the address of the I/O port and the data to send. The instruction <code>in</code> takes a single parameter, the address of the I/O port, and returns data from the hardware. One can think of I/O ports as communicating with hardware the same way as you communicate with a server using sockets. The cursor (the blinking rectangle) of the framebuffer is one example of hardware controlled via I/O ports on a PC.</p>
<h2 id="the-framebuffer"><span class="header-section-number">4.2</span> The Framebuffer</h2>
<p>The framebuffer is a hardware device that is capable of displaying a buffer of memory on the screen <span class="citation">[26]</span>. The framebuffer has 80 columns and 25 rows, and the row and column indices start at 0 (so rows are labelled 0 - 24).</p>
<h3 id="writing-text"><span class="header-section-number">4.2.1</span> Writing Text</h3>
<p>Writing text to the console via the framebuffer is done with memory-mapped I/O. The starting address of the memory-mapped I/O for the framebuffer is <code>0x000B8000</code> <span class="citation">[27]</span>. The memory is divided into 16 bit cells, where the 16 bits determine both the character, the foreground color and the background color. The highest eight bits is the ASCII <span class="citation">[28]</span> value of the character, bit 7 - 4 the background and bit 3 - 0 the foreground, as can be seen in the following figure:</p>
<pre><code>Bit: | 15 14 13 12 11 10 9 8 | 7 6 5 4 | 3 2 1 0 |
Content: | ASCII | FG | BG |</code></pre>
<p>The available colors are shown in the following table:</p>
<table>
<thead>
<tr class="header">
<th align="right">Color</th>
<th align="left">Value</th>
<th align="right">Color</th>
<th align="left">Value</th>
<th align="right">Color</th>
<th align="left">Value</th>
<th align="right">Color</th>
<th align="left">Value</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="right">Black</td>
<td align="left">0</td>
<td align="right">Red</td>
<td align="left">4</td>
<td align="right">Dark grey</td>
<td align="left">8</td>
<td align="right">Light red</td>
<td align="left">12</td>
</tr>
<tr class="even">
<td align="right">Blue</td>
<td align="left">1</td>
<td align="right">Magenta</td>
<td align="left">5</td>
<td align="right">Light blue</td>
<td align="left">9</td>
<td align="right">Light magenta</td>
<td align="left">13</td>
</tr>
<tr class="odd">
<td align="right">Green</td>
<td align="left">2</td>
<td align="right">Brown</td>
<td align="left">6</td>
<td align="right">Light green</td>
<td align="left">10</td>
<td align="right">Light brown</td>
<td align="left">14</td>
</tr>
<tr class="even">
<td align="right">Cyan</td>
<td align="left">3</td>
<td align="right">Light grey</td>
<td align="left">7</td>
<td align="right">Light cyan</td>
<td align="left">11</td>
<td align="right">White</td>
<td align="left">15</td>
</tr>
</tbody>
</table>
<p>The first cell corresponds to row zero, column zero on the console. Using an ASCII table, one can see that A corresponds to 65 or <code>0x41</code>. Therefore, to write the character A with a green foreground (2) and dark grey background (8) at place (0,0), the following assembly code instruction is used:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> <span class="kw">mov</span> [<span class="bn">0x000B8000</span>]<span class="bn">, 0x4128</span></code></pre>
<p>The second cell then corresponds to row zero, column one and its address is therefore:</p>
<pre><code> 0x000B8000 + 16 = 0x000B8010</code></pre>
<p>Writing to the framebuffer can also be done in C by treating the address <code>0x000B8000</code> as a char pointer, <code>char *fb = (char *) 0x000B8000</code>. Then, writing A at place (0,0) with green foreground and dark grey background becomes:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> fb[<span class="dv">0</span>] = <span class="st">'A'</span><span class="co">;</span>
fb[<span class="dv">1</span>] = <span class="bn">0x28</span><span class="co">;</span></code></pre>
<p>The following code shows how this can be wrapped into a function:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="co">/** fb_write_cell:</span>
<span class="co"> * Writes a character with the given foreground and background to position i</span>
<span class="co"> * in the framebuffer.</span>
<span class="co"> *</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">i</span><span class="co"> The location in the framebuffer</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">c</span><span class="co"> The character</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">fg</span><span class="co"> The foreground color</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">bg</span><span class="co"> The background color</span>
<span class="co"> */</span>
<span class="dt">void</span> fb_write_cell(<span class="dt">unsigned</span> <span class="dt">int</span> i, <span class="dt">char</span> c, <span class="dt">unsigned</span> <span class="dt">char</span> fg, <span class="dt">unsigned</span> <span class="dt">char</span> bg)
{
fb[i] = c;
fb[i + <span class="dv">1</span>] = ((fg & <span class="bn">0x0F</span>) << <span class="dv">4</span>) | (bg & <span class="bn">0x0F</span>)
}</code></pre>
<p>The function can then be used as follows:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="ot">#define FB_GREEN 2</span>
<span class="ot">#define FB_DARK_GREY 8</span>
fb_write_cell(<span class="dv">0</span>, 'A', FB_GREEN, FB_DARK_GREY);</code></pre>
<h3 id="moving-the-cursor"><span class="header-section-number">4.2.2</span> Moving the Cursor</h3>
<p>Moving the cursor of the framebuffer is done via two different I/O ports. The cursor’s position is determined with a 16 bits integer: 0 means row zero, column zero; 1 means row zero, column one; 80 means row one, column zero and so on. Since the position is 16 bits large, and the <code>out</code> assembly code instruction argument is 8 bits, the position must be sent in two turns, first 8 bits then the next 8 bits. The framebuffer has two I/O ports, one for accepting the data, and one for describing the data being received. Port <code>0x3D4</code> <span class="citation">[29]</span> is the port that describes the data and port <code>0x3D5</code> <span class="citation">[29]</span> is for the data itself.</p>
<p>To set the cursor at row one, column zero (position <code>80 = 0x0050</code>), one would use the following assembly code instructions:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> <span class="kw">out</span> <span class="bn">0x3D4</span>, <span class="dv">14</span> <span class="co">; 14 tells the framebuffer to expect the highest 8 bits of the position</span>
<span class="kw">out</span> <span class="bn">0x3D5, 0x00 </span><span class="co">; sending the highest 8 bits of 0x0050</span>
<span class="kw">out</span> <span class="bn">0x3D4</span>, <span class="dv">15</span> <span class="co">; 15 tells the framebuffer to expect the lowest 8 bits of the position</span>
<span class="kw">out</span> <span class="bn">0x3D5, 0x50 </span><span class="co">; sending the lowest 8 bits of 0x0050</span></code></pre>
<p>The <code>out</code> assembly code instruction can’t be executed directly in C. Therefore it is a good idea to wrap <code>out</code> in a function in assembly code which can be accessed from C via the cdecl calling standard <span class="citation">[25]</span>:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> <span class="kw">global</span> outb <span class="co">; make the label outb visible outside this file</span>
<span class="co">; outb - send a byte to an I/O port</span>
<span class="co">; stack: [esp + 8] the data byte</span>
<span class="co">; [esp + 4] the I/O port</span>
<span class="co">; [esp ] return address</span>
<span class="fu"> outb:</span>
<span class="kw">mov</span> <span class="kw">al</span>, [<span class="kw">esp</span> + <span class="dv">8</span>] <span class="co">; move the data to be sent into the al register</span>
<span class="kw">mov</span> <span class="kw">dx</span>, [<span class="kw">esp</span> + <span class="dv">4</span>] <span class="co">; move the address of the I/O port into the dx register</span>
<span class="kw">out</span> <span class="kw">dx</span>, <span class="kw">al</span> <span class="co">; send the data to the I/O port</span>
<span class="kw">ret</span> <span class="co">; return to the calling function</span></code></pre>
<p>By storing this function in a file called <code>io.s</code> and also creating a header <code>io.h</code>, the <code>out</code> assembly code instruction can be conveniently accessed from C:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="ot">#ifndef INCLUDE_IO_H</span>
<span class="ot">#define INCLUDE_IO_H</span>
<span class="co">/** outb:</span>
<span class="co"> * Sends the given data to the given I/O port. Defined in io.s</span>
<span class="co"> *</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">port</span><span class="co"> The I/O port to send the data to</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">data</span><span class="co"> The data to send to the I/O port</span>
<span class="co"> */</span>
<span class="dt">void</span> outb(<span class="dt">unsigned</span> <span class="dt">short</span> port, <span class="dt">unsigned</span> <span class="dt">char</span> data);
<span class="ot">#endif </span><span class="co">/* INCLUDE_IO_H */</span></code></pre>
<p>Moving the cursor can now be wrapped in a C function:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="ot">#include "io.h"</span>
<span class="co">/* The I/O ports */</span>
<span class="ot">#define FB_COMMAND_PORT 0x3D4</span>
<span class="ot">#define FB_DATA_PORT 0x3D5</span>
<span class="co">/* The I/O port commands */</span>
<span class="ot">#define FB_HIGH_BYTE_COMMAND 14</span>
<span class="ot">#define FB_LOW_BYTE_COMMAND 15</span>
<span class="co">/** fb_move_cursor:</span>
<span class="co"> * Moves the cursor of the framebuffer to the given position</span>
<span class="co"> *</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">pos</span><span class="co"> The new position of the cursor</span>
<span class="co"> */</span>
<span class="dt">void</span> fb_move_cursor(<span class="dt">unsigned</span> <span class="dt">short</span> pos)
{
outb(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND);
outb(FB_DATA_PORT, ((pos >> <span class="dv">8</span>) & <span class="bn">0x00FF</span>));
outb(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND);
outb(FB_DATA_PORT, pos & <span class="bn">0x00FF</span>);
}</code></pre>
<h3 id="the-driver"><span class="header-section-number">4.2.3</span> The Driver</h3>
<p>The driver should provide an interface that the rest of the code in the OS will use for interacting with the framebuffer. There is no right or wrong in what functionality the interface should provide, but a suggestion is to have a <code>write</code> function with the following declaration:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="dt">int</span> write(<span class="dt">char</span> *buf, <span class="dt">unsigned</span> <span class="dt">int</span> len);</code></pre>
<p>The <code>write</code> function writes the contents of the buffer <code>buf</code> of length <code>len</code> to the screen. The <code>write</code> function should automatically advance the cursor after a character has been written and scroll the screen if necessary.</p>
<h2 id="the-serial-ports"><span class="header-section-number">4.3</span> The Serial Ports</h2>
<p>The serial port <span class="citation">[30]</span> is an interface for communicating between hardware devices and although it is available on almost all motherboards, it is seldom exposed to the user in the form of a DE-9 connector nowadays. The serial port is easy to use, and, more importantly, it can be used as a logging utility in Bochs. If a computer has support for a serial port, then it usually has support for multiple serial ports, but we will only make use of one of the ports. This is because we will only use the serial ports for logging. Furthermore, we will only use the serial ports for output, not input. The serial ports are completely controlled via I/O ports.</p>
<h3 id="configuring-the-serial-port"><span class="header-section-number">4.3.1</span> Configuring the Serial Port</h3>
<p>The first data that need to be sent to the serial port is configuration data. In order for two hardware devices to be able to talk to each other they must agree upon a couple of things. These things include:</p>
<ul>
<li>The speed used for sending data (bit or baud rate)</li>
<li>If any error checking should be used for the data (parity bit, stop bits)</li>
<li>The number of bits that represent a unit of data (data bits)</li>
</ul>
<h3 id="configuring-the-line"><span class="header-section-number">4.3.2</span> Configuring the Line</h3>
<p>Configuring the line means to configure how data is being sent over the line. The serial port has an I/O port, the <em>line command port</em>, that is used for configuration.</p>
<p>First the speed for sending data will be set. The serial port has an internal clock that runs at 115200 Hz. Setting the speed means sending a divisor to the serial port, for example sending 2 results in a speed of <code>115200 / 2 = 57600</code> Hz.</p>
<p>The divisor is a 16 bit number but we can only send 8 bits at a time. We must therefore send an instruction telling the serial port to first expect the highest 8 bits, then the lowest 8 bits. This is done by sending <code>0x80</code> to the line command port. An example is shown below:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="ot">#include "io.h" </span><span class="co">/* io.h is implement in the section "Moving the cursor" */</span>
<span class="co">/* The I/O ports */</span>
<span class="co">/* All the I/O ports are calculated relative to the data port. This is because</span>
<span class="co"> * all serial ports (COM1, COM2, COM3, COM4) have their ports in the same</span>
<span class="co"> * order, but they start at different values.</span>
<span class="co"> */</span>
<span class="ot">#define SERIAL_COM1_BASE 0x3F8 </span><span class="co">/* COM1 base port */</span>
<span class="ot">#define SERIAL_DATA_PORT(base) (base)</span>
<span class="ot">#define SERIAL_FIFO_COMMAND_PORT(base) (base + 2)</span>
<span class="ot">#define SERIAL_LINE_COMMAND_PORT(base) (base + 3)</span>
<span class="ot">#define SERIAL_MODEM_COMMAND_PORT(base) (base + 4)</span>
<span class="ot">#define SERIAL_LINE_STATUS_PORT(base) (base + 5)</span>
<span class="co">/* The I/O port commands */</span>
<span class="co">/* SERIAL_LINE_ENABLE_DLAB:</span>
<span class="co"> * Tells the serial port to expect first the highest 8 bits on the data port,</span>
<span class="co"> * then the lowest 8 bits will follow</span>
<span class="co"> */</span>
<span class="ot">#define SERIAL_LINE_ENABLE_DLAB 0x80</span>
<span class="co">/** serial_configure_baud_rate:</span>
<span class="co"> * Sets the speed of the data being sent. The default speed of a serial</span>
<span class="co"> * port is 115200 bits/s. The argument is a divisor of that number, hence</span>
<span class="co"> * the resulting speed becomes (115200 / divisor) bits/s.</span>
<span class="co"> *</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">com</span><span class="co"> The COM port to configure</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">divisor</span><span class="co"> The divisor</span>
<span class="co"> */</span>
<span class="dt">void</span> serial_configure_baud_rate(<span class="dt">unsigned</span> <span class="dt">short</span> com, <span class="dt">unsigned</span> <span class="dt">short</span> divisor)
{
outb(SERIAL_LINE_COMMAND_PORT(com),
SERIAL_LINE_ENABLE_DLAB);
outb(SERIAL_DATA_PORT(com),
(divisor >> <span class="dv">8</span>) & <span class="bn">0x00FF</span>);
outb(SERIAL_DATA_PORT(com),
divisor & <span class="bn">0x00FF</span>);
}</code></pre>
<p>The way that data should be sent must be configured. This is also done via the line command port by sending a byte. The layout of the 8 bits looks like the following:</p>
<pre><code>Bit: | 7 | 6 | 5 4 3 | 2 | 1 0 |
Content: | d | b | prty | s | dl |</code></pre>
<p>A description for each name can be found in the table below (and in <span class="citation">[31]</span>):</p>
<table>
<thead>
<tr class="header">
<th align="right">Name</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="right">d</td>
<td align="left">Enables (<code>d = 1</code>) or disables (<code>d = 0</code>) DLAB</td>
</tr>
<tr class="even">
<td align="right">b</td>
<td align="left">If break control is enabled (<code>b = 1</code>) or disabled (<code>b = 0</code>)</td>
</tr>
<tr class="odd">
<td align="right">prty</td>
<td align="left">The number of parity bits to use</td>
</tr>
<tr class="even">
<td align="right">s</td>
<td align="left">The number of stop bits to use (<code>s = 0</code> equals 1, <code>s = 1</code> equals 1.5 or 2)</td>
</tr>
<tr class="odd">
<td align="right">dl</td>
<td align="left">Describes the length of the data</td>
</tr>
</tbody>
</table>
<p>We will use the mostly standard value <code>0x03</code> <span class="citation">[31]</span>, meaning a length of 8 bits, no parity bit, one stop bit and break control disabled. This is sent to the line command port, as seen in the following example:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="co">/** serial_configure_line:</span>
<span class="co"> * Configures the line of the given serial port. The port is set to have a</span>
<span class="co"> * data length of 8 bits, no parity bits, one stop bit and break control</span>
<span class="co"> * disabled.</span>
<span class="co"> *</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">com</span><span class="co"> The serial port to configure</span>
<span class="co"> */</span>
<span class="dt">void</span> serial_configure_line(<span class="dt">unsigned</span> <span class="dt">short</span> com)
{
<span class="co">/* Bit: | 7 | 6 | 5 4 3 | 2 | 1 0 |</span>
<span class="co"> * Content: | d | b | prty | s | dl |</span>
<span class="co"> * Value: | 0 | 0 | 0 0 0 | 0 | 1 1 | = 0x03</span>
<span class="co"> */</span>
outb(SERIAL_LINE_COMMAND_PORT(com), <span class="bn">0x03</span>);
}</code></pre>
<p>The article on OSDev <span class="citation">[31]</span> has a more in-depth explanation of the values.</p>
<h3 id="configuring-the-buffers"><span class="header-section-number">4.3.3</span> Configuring the Buffers</h3>
<p>When data is transmitted via the serial port it is placed in buffers, both when receiving and sending data. This way, if you send data to the serial port faster than it can send it over the wire, it will be buffered. However, if you send too much data too fast the buffer will be full and data will be lost. In other words, the buffers are FIFO queues. The FIFO queue configuration byte looks like the following figure:</p>
<pre><code>Bit: | 7 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Content: | lvl | bs | r | dma | clt | clr | e |</code></pre>
<p>A description for each name can be found in the table below:</p>
<table>
<thead>
<tr class="header">
<th align="right">Name</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="right">lvl</td>
<td align="left">How many bytes should be stored in the FIFO buffers</td>
</tr>
<tr class="even">
<td align="right">bs</td>
<td align="left">If the buffers should be 16 or 64 bytes large</td>
</tr>
<tr class="odd">
<td align="right">r</td>
<td align="left">Reserved for future use</td>
</tr>
<tr class="even">
<td align="right">dma</td>
<td align="left">How the serial port data should be accessed</td>
</tr>
<tr class="odd">
<td align="right">clt</td>
<td align="left">Clear the transmission FIFO buffer</td>
</tr>
<tr class="even">
<td align="right">clr</td>
<td align="left">Clear the receiver FIFO buffer</td>
</tr>
<tr class="odd">
<td align="right">e</td>
<td align="left">If the FIFO buffer should be enabled or not</td>
</tr>
</tbody>
</table>
<p>We use the value <code>0xC7 = 11000111</code> that:</p>
<ul>
<li>Enables FIFO</li>
<li>Clear both receiver and transmission FIFO queues</li>
<li>Use 14 bytes as size of queue</li>
</ul>
<p>The WikiBook on serial programming <span class="citation">[32]</span> explains the values in more depth.</p>
<h3 id="configuring-the-modem"><span class="header-section-number">4.3.4</span> Configuring the Modem</h3>
<p>The modem control register is used for very simple hardware flow control via the Ready To Transmit (RTS) and Data Terminal Ready (DTR) pins. When configuring the serial port we want RTS and DTR to be 1, which means that we are ready to send data.</p>
<p>The modem configuration byte is shown in the following figure:</p>
<pre><code>Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Content: | r | r | af | lb | ao2 | ao1 | rts | dtr |</code></pre>
<p>A description for each name can be found in the table below:</p>
<table>
<thead>
<tr class="header">
<th align="right">Name</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="right">r</td>
<td align="left">Reserved</td>
</tr>
<tr class="even">
<td align="right">af</td>
<td align="left">Autoflow control enabled</td>
</tr>
<tr class="odd">
<td align="right">lb</td>
<td align="left">Loopback mode (used for debugging serial ports)</td>
</tr>
<tr class="even">
<td align="right">ao2</td>
<td align="left">Auxiliary output 2, used for receiving interrupts</td>
</tr>
<tr class="odd">
<td align="right">ao1</td>
<td align="left">Auxiliary output 1</td>
</tr>
<tr class="even">
<td align="right">rts</td>
<td align="left">Ready To Transmit</td>
</tr>
<tr class="odd">
<td align="right">dtr</td>
<td align="left">Data Terminal Ready</td>
</tr>
</tbody>
</table>
<p>We don’t need to enable interrupts, because we won’t handle any received data. Therefore we use the configuration value <code>0x03 = 00000011</code> (RTS = 1 and DTS = 1).</p>
<h3 id="writing-data-to-the-serial-port"><span class="header-section-number">4.3.5</span> Writing Data to the Serial Port</h3>
<p>Writing data to the serial port is done via the data I/O port. However, before writing, the transmit FIFO queue has to be empty (all previous writes must have finished). The transmit FIFO queue is empty if bit 5 of the line status I/O port is equal to one.</p>
<p>Reading the contents of an I/O port is done via the <code>in</code> assembly code instruction. There is no way to use the <code>in</code> assembly code instruction from C, therefore it has to be wrapped (the same way as the <code>out</code> assembly code instruction):</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> <span class="kw">global</span> inb
<span class="co">; inb - returns a byte from the given I/O port</span>
<span class="co">; stack: [esp + 4] The address of the I/O port</span>
<span class="co">; [esp ] The return address</span>
<span class="fu"> inb:</span>
<span class="kw">mov</span> <span class="kw">dx</span>, [<span class="kw">esp</span> + <span class="dv">4</span>] <span class="co">; move the address of the I/O port to the dx register</span>
<span class="kw">in</span> <span class="kw">al</span>, <span class="kw">dx</span> <span class="co">; read a byte from the I/O port and store it in the al register</span>
<span class="kw">ret</span> <span class="co">; return the read byte</span></code></pre>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="co">/* in file io.h */</span>
<span class="co">/** inb:</span>
<span class="co"> * Read a byte from an I/O port.</span>
<span class="co"> *</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">port</span><span class="co"> The address of the I/O port</span>
<span class="co"> * </span><span class="kw">@return</span><span class="co"> The read byte</span>
<span class="co"> */</span>
<span class="dt">unsigned</span> <span class="dt">char</span> inb(<span class="dt">unsigned</span> <span class="dt">short</span> port);</code></pre>
<p>Checking if the transmit FIFO is empty can then be done from C:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="ot">#include "io.h"</span>
<span class="co">/** serial_is_transmit_fifo_empty:</span>
<span class="co"> * Checks whether the transmit FIFO queue is empty or not for the given COM</span>
<span class="co"> * port.</span>
<span class="co"> *</span>
<span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">com</span><span class="co"> The COM port</span>
<span class="co"> * </span><span class="kw">@return</span><span class="co"> 0 if the transmit FIFO queue is not empty</span>
<span class="co"> * 1 if the transmit FIFO queue is empty</span>
<span class="co"> */</span>
<span class="dt">int</span> serial_is_transmit_fifo_empty(<span class="dt">unsigned</span> <span class="dt">int</span> com)
{
<span class="co">/* 0x20 = 0010 0000 */</span>
<span class="kw">return</span> inb(SERIAL_LINE_STATUS_PORT(com)) & <span class="bn">0x20</span>;
}</code></pre>
<p>Writing to a serial port means spinning as long as the transmit FIFO queue isn’t empty, and then writing the data to the data I/O port.</p>
<h3 id="configuring-bochs"><span class="header-section-number">4.3.6</span> Configuring Bochs</h3>
<p>To save the output from the first serial serial port the Bochs configuration file <code>bochsrc.txt</code> must be updated. The <code>com1</code> configuration instructs Bochs how to handle first serial port:</p>
<pre><code> com1: enabled=1, mode=file, dev=com1.out</code></pre>
<p>The output from serial port one will now be stored in the file <code>com1.out</code>.</p>
<h3 id="the-driver-1"><span class="header-section-number">4.3.7</span> The Driver</h3>
<p>We recommend that you implement a <code>write</code> function for the serial port similar to the <code>write</code> function in the driver for the framebuffer. To avoid name clashes with the <code>write</code> function for the framebuffer it is a good idea to name the functions <code>fb_write</code> and <code>serial_write</code> to distinguish them.</p>
<p>We further recommend that you try to write a <code>printf</code>-like function, see section 7.3 in <span class="citation">[8]</span>. The <code>printf</code> function could take an additional argument to decide to which device to write the output (framebuffer or serial).</p>
<p>A final recommendation is that you create some way of distinguishing the severeness of the log messages, for example by prepending the messages with <code>DEBUG</code>, <code>INFO</code> or <code>ERROR</code>.</p>
<h2 id="further-reading-2"><span class="header-section-number">4.4</span> Further Reading</h2>
<ul>
<li>The book “Serial programming” (available on WikiBooks) has a great section on programming the serial port, <a href="http://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming#UART_Registers">http://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming#UART_Registers</a></li>
<li>The OSDev wiki has a page with a lot of information about the serial ports, <a href="http://wiki.osdev.org/Serial_ports">http://wiki.osdev.org/Serial_ports</a></li>
</ul>
<h1 id="segmentation"><span class="header-section-number">5</span> Segmentation</h1>
<p><em>Segmentation</em> in x86 means accessing the memory through segments. Segments are portions of the address space, possibly overlapping, specified by a base address and a limit. To address a byte in segmented memory you use a 48-bit <em>logical address</em>: 16 bits that specifies the segment and 32-bits that specifies what offset within that segment you want. The offset is added to the base address of the segment, and the resulting linear address is checked against the segment’s limit - see the figure below. If everything works out fine (including access-rights checks ignored for now) the result is a <em>linear address</em>. When paging is disabled, then the linear address space is mapped 1:1 onto the <em>physical address</em> space, and the physical memory can be accessed. (See the chapter <a href="#paging">“Paging”</a> for how to enable paging.)</p>
<div class="figure">
<img src="images/intel_3_5_logical_to_linear.png" alt="Translation of logical addresses to linear addresses." /><p class="caption">Translation of logical addresses to linear addresses.</p>
</div>
<p>To enable segmentation you need to set up a table that describes each segment - a <em>segment descriptor table</em>. In x86, there are two types of descriptor tables: the <em>Global Descriptor Table</em> (GDT) and <em>Local Descriptor Tables</em> (LDT). An LDT is set up and managed by user-space processes, and all processes have their own LDT. LDTs can be used if a more complex segmentation model is desired - we won’t use it. The GDT is shared by everyone - it’s global.</p>
<p>As we discuss in the sections on virtual memory and paging, segmentation is rarely used more than in a minimal setup, similar to what we do below.</p>
<h2 id="accessing-memory"><span class="header-section-number">5.1</span> Accessing Memory</h2>
<p>Most of the time when accessing memory there is no need to explicitly specify the segment to use. The processor has six 16-bit segment registers: <code>cs</code>, <code>ss</code>, <code>ds</code>, <code>es</code>, <code>gs</code> and <code>fs</code>. The register <code>cs</code> is the code segment register and specifies the segment to use when fetching instructions. The register <code>ss</code> is used whenever accessing the stack (through the stack pointer <code>esp</code>), and <code>ds</code> is used for other data accesses. The OS is free to use the registers <code>es</code>, <code>gs</code> and <code>fs</code> however it want.</p>
<p>Below is an example showing implicit use of the segment registers:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"><span class="fu"> func:</span>
<span class="kw">mov</span> <span class="kw">eax</span>, [<span class="kw">esp</span><span class="dv">+4</span>]
<span class="kw">mov</span> <span class="kw">ebx</span>, [<span class="kw">eax</span>]
<span class="kw">add</span> <span class="kw">ebx</span>, <span class="dv">8</span>
<span class="kw">mov</span> [<span class="kw">eax</span>], <span class="kw">ebx</span>
<span class="kw">ret</span></code></pre>
<p>The above example can be compared with the following one that makes explicit use of the segment registers:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"><span class="fu"> func:</span>
<span class="kw">mov</span> <span class="kw">eax</span>, [<span class="kw">ss</span>:<span class="kw">esp</span><span class="dv">+4</span>]
<span class="kw">mov</span> <span class="kw">ebx</span>, [<span class="kw">ds</span>:<span class="kw">eax</span>]
<span class="kw">add</span> <span class="kw">ebx</span>, <span class="dv">8</span>
<span class="kw">mov</span> [<span class="kw">ds</span>:<span class="kw">eax</span>], <span class="kw">ebx</span>
<span class="kw">ret</span></code></pre>
<p>You don’t need to use <code>ss</code> for storing the stack segment selector, or <code>ds</code> for the data segment selector. You could store the stack segment selector in <code>ds</code> and vice versa. However, in order to use the implicit style shown above, you must store the segment selectors in their indented registers.</p>
<p>Segment descriptors and their fields are described in figure 3-8 in the Intel manual <span class="citation">[33]</span>.</p>
<h2 id="the-global-descriptor-table-gdt"><span class="header-section-number">5.2</span> The Global Descriptor Table (GDT)</h2>
<p>A GDT/LDT is an array of 8-byte segment descriptors. The first descriptor in the GDT is always a null descriptor and can never be used to access memory. At least two segment descriptors (plus the null descriptor) are needed for the GDT, because the descriptor contains more information than just the base and limit fields. The two most relevant fields for us are the <em>Type</em> field and the <em>Descriptor Privilege Level</em> (DPL) field.</p>
<p>Table 3-1 in chapter 3 of the Intel manual <span class="citation">[33]</span> specifies the values for the Type field. The table shows that the Type field can’t be both writable <em>and</em> executable at the same time. Therefore, two segments are needed: one segment for executing code to put in <code>cs</code> (Type is Execute-only or Execute-Read) and one segment for reading and writing data (Type is Read/Write) to put in the other segment registers.</p>
<p>The DPL specifies the <em>privilege levels</em> required to use the segment. x86 allows for four privilege levels (PL), 0 to 3, where PL0 is the most privileged. In most operating systems (eg. Linux and Windows), only PL0 and PL3 are used. However, some operating system, such as MINIX, make use of all levels. The kernel should be able to do anything, therefore it uses segments with DPL set to 0 (also called kernel mode). The current privilege level (CPL) is determined by the segment selector in <code>cs</code>.</p>
<p>The segments needed are described in the table below.</p>
<table>
<caption>The segment descriptors needed.</caption>
<thead>
<tr class="header">
<th align="right">Index</th>
<th align="right">Offset</th>
<th align="left">Name</th>
<th align="left">Address range</th>
<th align="left">Type</th>
<th align="left">DPL</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="right">0</td>
<td align="right"><code>0x00</code></td>
<td align="left">null descriptor</td>
<td align="left"></td>
<td align="left"></td>
<td align="left"></td>
</tr>
<tr class="even">
<td align="right">1</td>
<td align="right"><code>0x08</code></td>
<td align="left">kernel code segment</td>
<td align="left"><code>0x00000000 - 0xFFFFFFFF</code></td>
<td align="left">RX</td>
<td align="left">PL0</td>
</tr>
<tr class="odd">
<td align="right">2</td>
<td align="right"><code>0x10</code></td>
<td align="left">kernel data segment</td>
<td align="left"><code>0x00000000 - 0xFFFFFFFF</code></td>
<td align="left">RW</td>
<td align="left">PL0</td>
</tr>
</tbody>
</table>
<p>Note that the segments overlap - they both encompass the entire linear address space. In our minimal setup we’ll only use segmentation to get privilege levels. See the Intel manual <span class="citation">[33]</span>, chapter 3, for details on the other descriptor fields.</p>
<h2 id="loading-the-gdt"><span class="header-section-number">5.3</span> Loading the GDT</h2>
<p>Loading the GDT into the processor is done with the <code>lgdt</code> assembly code instruction, which takes the address of a struct that specifies the start and size of the GDT. It is easiest to encode this information using a <a href="#packing-structs">“packed struct”</a> as shown in the following example:</p>
<pre class="sourceCode c"><code class="sourceCode c"> <span class="kw">struct</span> gdt {
<span class="dt">unsigned</span> <span class="dt">int</span> address;
<span class="dt">unsigned</span> <span class="dt">short</span> size;
} __attribute__((packed));</code></pre>
<p>If the content of the <code>eax</code> register is the address to such a struct, then the GDT can be loaded with the assembly code shown below:</p>
<pre class="sourceCode nasm"><code class="sourceCode nasm"> <span class="kw">lgdt</span> [<span class="kw">eax</span>]</code></pre>
<p>It might be easier if you make this instruction available from C, the same way as was done with the assembly code instructions <code>in</code> and <code>out</code>.</p>
<p>After the GDT has been loaded the segment registers needs to be loaded with their corresponding segment selectors. The content of a segment selector is described in the figure and table below:</p>
<pre><code>Bit: | 15 3 | 2 | 1 0 |
Content: | offset (index) | ti | rpl |</code></pre>
<table>
<caption>The layout of segment selectors.</caption>
<col width="23%" />
<col width="76%" />
<thead>
<tr class="header">
<th align="left">Name</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left">rpl</td>
<td align="left">Requested Privilege Level - we want to execute in PL0 for now.</td>
</tr>
<tr class="even">
<td align="left">ti</td>
<td align="left">Table Indicator. 0 means that this specifies a GDT segment, 1 means an LDT Segment.</td>
</tr>
<tr class="odd">
<td align="left">offset (index)</td>
<td align="left">Offset within descriptor table.</td>
</tr>
</tbody>
</table>
<p>The offset of the segment selector is added to the start of the GDT to get the address of the segment descriptor: <code>0x08</code> for the first descriptor and <code>0x10</code> for the second, since each descriptor is 8 bytes. The Requested Privilege Level (RPL) should be <code>0</code> since the kernel of the OS should execute in privilege level 0.</p>
<p>Loading the segment selector registers is easy for the data registers - just copy the correct offsets to the registers:</p>