-
Notifications
You must be signed in to change notification settings - Fork 0
/
labs.tex
1307 lines (1007 loc) · 42.5 KB
/
labs.tex
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
%% LyX 2.3.7 created this file. For more info, see http://www.lyx.org/.
%% Do not edit unless you really know what you are doing.
\documentclass[oneside,english,latin9,utf8]{extbook}
\usepackage[T1]{fontenc}
\usepackage[latin9]{inputenc}
\setcounter{secnumdepth}{3}
\setcounter{tocdepth}{3}
\usepackage{babel}
\usepackage{float}
\usepackage{url}
\usepackage{graphicx}
\usepackage[unicode=true,pdfusetitle,
bookmarks=true,bookmarksnumbered=false,bookmarksopen=false,
breaklinks=false,pdfborder={0 0 1},backref=false,colorlinks=false]
{hyperref}
\makeatletter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
\newenvironment{lyxlist}[1]
{\begin{list}{}
{\settowidth{\labelwidth}{#1}
\setlength{\leftmargin}{\labelwidth}
\addtolength{\leftmargin}{\labelsep}
\renewcommand{\makelabel}[1]{##1\hfil}}}
{\end{list}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
\usepackage[encapsulated]{CJK}
\usepackage{minted}
\usepackage[dvipsnames]{xcolor}
\usepackage{CJKutf8}
\usepackage[overlap, CJK]{ruby}
\newenvironment{SChinese}{%
\CJKfamily{gbsn}%
\CJKtilde
\CJKnospace}{}
\newcommand{\cntxt}[1]{\begin{CJK}{UTF8}{}\begin{SChinese}#1\end{SChinese}\end{CJK}}
\makeatother
\usepackage{minted}
\setminted{frame=lines,
breaklines=true,
bgcolor={gray!5},
numbers=left,
breaksymbol={},
fontfamily=tt,
tabsize=4}
\renewcommand{\listingscaption}{Listing}
\begin{document}
\title{The Guide to ChronOS}
\author{Betty Liu u2061245}
\maketitle
\tableofcontents{}
\chapter{Introduction to ChronOS}
All code available at GitHub\url{https://github.com/acyanbird/chronos_labs/}
Please see different branch for each stage.
Writing a lab assignment to create an operating system using Rust
as the programming language:
\textbf{Lab Assignment: }Building an Operating System with Rust
\textbf{Objective:} In this lab, you will learn to create a simple
operating system using the Rust programming language. Operating systems
are complex pieces of software that manage hardware resources and
provide services to other software applications. This lab will introduce
you to the basics of operating system development, focusing on the
foundational components.
.
\chapter{Lab 0 - Introduction to Rust Programming languoage}
\section{Objectives}
Since this project uses Rust as the programming language, and there
are no specific university courses for it, this lab will introduce
you to some fundamental syntax to help you get started. You don't
need to read through this Lab completely right now. Throughout the
subsequent implementation of the operating system, if you encounter
new Rust content, we will reference you back to the relevant sections
of Lab 0. Combining examples will help you better understand their
application.
\section{Install Rust\label{sec:Install-Rust}}
Rustup is used to install and managed Rust. You can check if your
machine is already install Rust by typing rustc -{}-version in your
console.
If not, for installation on Unix-like machine (e.g. MacOS, Linux)
input this in terminal
\begin{minted}[fontfamily=rm,fontsize={\footnotesize},breaklines=true]{bash}
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
\end{minted}
For windows users install
\begin{minted}{bash}
https://static.rust-lang.org/rustup/dist/i686-pc-windows-gnu/
rustup-init.exe
\end{minted}
\subsection{Integrated Development Environment}
I highly recommend using an IDE for development. Currently, there
are not many IDEs that support Rust. Here, I recommend \href{https://code.visualstudio.com/}{Visual Studio Code}
+ \href{https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer}{rust-analyzer entension}
or \href{https://www.jetbrains.com/zh-cn/rust/}{RustRover}.
\section{Hello World}
We will use cargo to create the basic framework for the project. Cargo
is Rust\textquoteright s build system and package manager and it should
be installed by rustup. You could check it by
\begin{minted}{rust}
cargo --version
\end{minted}
Create project by
\begin{minted}{rust}
cargo new hello
cd hello
\end{minted}
It will also generate an empty git repository for version control,
and a cargo.toml that procvie project basic information and dependency.
We could ignore it right now. The file structure is like:
\begin{figure}[H]
\includegraphics{\string"/home/lucia/Pictures/Screenshots/Screenshot from 2024-01-11 23-19-08\string".png}
\caption{project structure}
\end{figure}
\begin{lyxlist}{00.00.0000}
\item [{Cargo.toml:}] This is the configuration file for your Rust project.
It contains metadata about the project, such as its name, version,
dependencies, and other settings.
\item [{src/directory:}] This directory is where you put your source code
files. It contains your project's main code. You will often have one
or more Rust source files (.rs) in this directory.
\item [{main.rs:}] This is the primary entry point for your Rust application.
It typically contains the main function, which is the starting point
of your program.
\end{lyxlist}
The main.rs created by cargo is a simple program that would print
Hello, world! It is similar to C. To run project use \textbf{cargo
run }command in terminal.
\begin{minted}{rust}
fn main() {
println!("Hello, world!");
}
\end{minted}
\section{Variables \label{sec:Variables}}
\textbf{Variables:}
\begin{itemize}
\item Variables in Rust are declared using the let keyword.
\item By default, variables in Rust are immutable, which means their values
cannot be changed once assigned. To make a variable mutable, you use
the mut keyword.
\item You can reassign values to mutable variables, but their types must
remain the same.
\begin{minted}{rust}
// Declare an immutable integer variable
let x = 10;
// Declare a mutable integer variable
let mut y = 20;
// Reassign a value to the mutable variable
y = 30;
\end{minted}
\end{itemize}
\textbf{Constants:}
\begin{itemize}
\item Constants in Rust are declared using the const keyword.
\item Constants must have an explicitly defined type and must have a fixed,
compile-time determined value.
\item Conventionally, constants are named using all uppercase letters and
underscores to separate words.
\end{itemize}
\begin{minted}{rust}
// Declare an integer constant
const MAX_VALUE: i32 = 100;
// Declare a string constant
const GREETING: &str = "Hello, Rust!";
\end{minted}
\section{Attributes\label{sec:Attributes}}
Attributes in Rust are metadata applied to modules, crates, functions,
structs, or other items. They can instruct the compiler to perform
specific tasks or apply certain properties to the item they annotate.
Attributes can be divided into two main categories: Inner Attributes
and Outer Attributes.
\begin{itemize}
\item Outer Attributes (\#{[}outer\_attribute{]}): Applied to the item that
follows them. They are used to set attributes or give instructions
related to the item directly below them.
\item Inner Attributes (\#!{[}inner\_attribute{]}): Applied to the item
they are contained within. They are often found at the beginning of
source files or modules to configure or set options for the scope
they reside in.
\end{itemize}
\section{Unsafe Rust\label{sec:Unsafe-Rust}}
\textbf{Tell the compiler I know what I'm doing!}
The unsafe keyword allows you to bypass the language's usual safety
checks and guarantees. It's used when you need to perform operations
that the Rust compiler can't prove to be safe at compile-time, such
as accessing raw pointers, dereferencing them, or making changes to
mutable static variables. It's a way to tell the Rust compiler that
you, the programmer, will ensure the safety of the code within the
unsafe block.
\subsection{External Code\label{subsec:External-Code}}
\section{Module\label{sec:Module}}
See more on \url{https://doc.rust-lang.org/rust-by-example/mod.html}
\section{Structs\label{sec:Structs}}
A struct, short for structure, is a custom data type that lets you
package together related data under a single name. We use it to making
code more organized, readable, and maintainable. It's similar to structs
in C. In our case
\begin{minted}{rust}
struct VGAChar {
ascii: u8,
color: u8,
}
\end{minted}
VGAChar is a struct with 2 fields, ascii and color both with data
type u8.
We can create instance by
\begin{minted}{rust}
let character = VGAChar {
ascii: b'A', // ASCII code for 'A'
color: 0x04; // black background, red foreground
};
\end{minted}
Using character.ascii and character.color access the ascii and color
fields of the character instance, respectively. You can use these
to read (and, if mutable, modify) the data stored in an instance of
a struct.
\section{Impl\label{subsec:Impl}}
TODO
It is a keyword used to implement functionality for a particular type,
such as a struct or enum. It allows you to define methods, associated
functions, and trait implementations for the specified type. The impl
keyword can be used indefinitely, but typically, organizing related
functionality into one or a few impl blocks is a clearer and more
maintainable practice.
\section{Function}
\section{Match\label{sec:Match}}
It allows you to compare a value against a series of patterns and
execute code based on which pattern matches. It's similar to a switch
statement in other languages but more powerful. For our example:
\begin{minted}{rust}
match byte {
// if not acceptable ASCII, print a space with error color
0x20..=0x7e | b'\n' => self.write_byte(byte, COLOR),
_ => self.write_byte(b' ', ERROR_COLOR),
}
\end{minted}
\begin{itemize}
\item 0x20..=0x7e | b'\textbackslash n': This is a pattern that matches
any byte within the range 0x20 to 0x7e or the newline character b'\textbackslash n'.
If the byte matches this pattern, self.write\_byte(byte, COLOR) is
executed.
\item \_: This is the wildcard pattern that matches any value that hasn't
been matched by the previous patterns. Then self.write\_byte(b' ',
ERROR\_COLOR) is executed, printing a space with error color.
\end{itemize}
\section{Trait}
In Rust, a trait defines a set of methods that types can implement.
It allows for defining shared behavior across different types.
\section{Self-study materials}
However, to master this language, you'll need to continue learning
as you go. Here are some recommended books and platforms for learning
Rust:
\begin{itemize}
\item \href{https://www.rust-lang.org/}{Official Rust Website}: The official
Rust website provides comprehensive documentation, tutorials, and
resources for learning Rust.
\item \href{https://doc.rust-lang.org/book/}{The Rust Programming Language}:
Affectionately nicknamed \textquotedblleft the book,\textquotedblright{}
The Rust Programming Language will give you an overview of the language
from first principles\cite{key-6}
\item \href{https://doc.rust-lang.org/rust-by-example/}{Rust by Example}:
Rust by Example (RBE) is a collection of runnable examples that illustrate
various Rust concepts and standard libraries.\cite{key-3} If you
prefer learning a new language by reading example code, then this
book might be more suitable for you.
\item \href{https://fasterthanli.me/articles/a-half-hour-to-learn-rust}{A half-hour to learn Rust}:
Providing lots of Rust snippets. It has brief explanation of each
code snippet, suitable for quick browsing.
\item \href{https://rustlings.cool/}{rustlings}: This project contains
small exercises to get you used to reading and writing Rust code.\cite{key-4}
\end{itemize}
\chapter{Lab 1 - Getting started}
These lab is running and tested on Linux (Debian) only for now.
\section{Expected Outcome}
In this lab, we'll create a Rust program entirely from scratch, free
from any reliance on a host operating system. Our goal is to develop
a minimal 64-bit kernel that can display text using VGA through QEMU.
Below is the expected output.
\section{Preparation}
\begin{itemize}
\item QEMU
\end{itemize}
To run the experiment's output, we need to use QEMU. Here, we won't
list the installation steps for QEMU on various operating systems.
Please visit the official website at \url{https://www.qemu.org/download/}
to download the appropriate installer and follow the on-screen instructions
for installation.
\begin{itemize}
\item Nightly Rust
\end{itemize}
If you are not install Rust already, please follow instructions here
\ref{sec:Install-Rust}
Rust offers various versions, but for operating system development,
we require certain experimental features not available in the stable
version. Thus, we can't use the stable version. To install Nightly
Rust, simply enter the following command in your terminal.
\begin{minted}{rust}
rustup update nightly
\end{minted}
\begin{itemize}
\item bootimage
\end{itemize}
This tool assists us in generating files for the virtual machine (QEMU).
To install it, use the following command in your terminal using Cargo.
\begin{minted}{rust}
cargo install bootimage
\end{minted}
\begin{itemize}
\item llvm-tools-preview
\end{itemize}
The llvm-tools-preview is a dependency for the bootimage tool. You
can install it using command
\begin{minted}{rust}
rustup component add llvm-tools-preview
\end{minted}
\section{Task 1 - Standalone Rust Binary}
\subsection{Introduction}
When we create a Rust program, similar to Lab 0, it usually relies
on an existing operating system. Rust comes with a standard library
that depends on the features of that operating system.
\begin{figure}[H]
\includegraphics[width=0.6\paperwidth]{\string"/home/lucia/Pictures/Screenshots/Screenshot from 2024-01-14 23-37-02\string".png}
\caption{Common Rust Program}
\end{figure}
But since our goal is to build an operating system from the ground
up, we can't rely on the existing one. So, we disable the standard
library using no\_std, and this lets us work directly with hardware.
\begin{figure}[H]
\includegraphics[scale=0.5]{\string"/home/lucia/Pictures/Screenshots/Screenshot from 2024-01-15 00-04-29\string".png}
\caption{Standalone Rust}
\end{figure}
See branch lab-1-1 for the code.
\subsection{Implementation}
\textbf{Step 1: Setup a New Rust Project}
Open a terminal and create a new Rust project by running
\begin{minted}[fontsize={\small},breaklines=true,showspaces=true]{tex}
cargo new chronos_lab --bin --edition 2018
\end{minted}
This creates a new binary project named chronos\_lab. You could use
your own name. Change into the project directory with
\begin{minted}{rust}
cd <your project name>
\end{minted}
\textbf{Step 2: Editing Cargo.toml}
Open the Cargo.toml file in your project's root directory.
In the initial Cargo.toml file, the {[}package{]} section includes
predefined name, version, and edition information. You can now leave
them unchanged.
Add configurations for development and release profiles to change
the panic strategy to abort, which disables stack unwinding during
a panic. Add these lines at the end of the Cargo.toml file:
\begin{minted}{rust}
[profile.dev]
panic = "abort" # Configures the compiler to abort the program on panic during development builds.
[profile.release]
panic = "abort" # Configures the compiler to abort the program on panic during release builds.
\end{minted}
Here is the detail explaination of this part:
\begin{itemize}
\item {[}profile.dev{]} and {[}profile.release{]}: These sections allow
you to specify settings for development (cargo build) and release
(cargo build -{}-release) profiles, respectively.
\item panic = \textquotedbl abort\textquotedbl : By default, Rust tries
to recover from errors (panics) by unwinding the program's stack,
which can't be done without additional support. In this case, we want
the program to just stop immediately when an error happens. Setting
panic = \textquotedbl abort\textquotedbl{} makes the program do that.
\end{itemize}
\textbf{Step 3: Writing the Freestanding Rust Code}
Open the src/main.rs file. Replace its contents with the following
code:
\begin{minted}{rust}
#![no_std] // disable the Rust standard library
#![no_main] // disable all Rust-level entry points
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn _start() {
loop {}
}
#[panic_handler] // this function is called on panic
fn panic(_info: &core::panic::PanicInfo) -> ! {
// the `!` type means "this function never returns"
// place holder for now, we'll write this function later
loop {}
}
\end{minted}
Here is the detail explaination of this part:
\begin{itemize}
\item \#!{[}no\_std{]}: This attribute disables the standard library. It
is used for low-level programming, where direct control over the system
is required.
\item \#!{[}no\_main{]}: Rust programs typically start execution from the
main function. This attribute disables it, which is necessary for
creating a freestanding binary.
\item \#{[}no\_mangle{]}: This attribute prevents Rust from changing the
name of the \_start function, ensuring the linker can find it.
\item pub extern \textquotedbl C\textquotedbl{} fn \_start() -> !: Defines
the entry point for our program. The function will use the C ABI for
compatibility with C code \ref{subsec:External-Code}. The ! return
type indicates that this function will never return.
\item \#{[}panic\_handler{]}: Specifies the function to call when a panic
occurs. Panics can occur for various reasons, such as out-of-bounds
array access.
\end{itemize}
For more information see \ref{sec:Attributes}
\textbf{Step 5: Building the Project}
By default, the linker includes the C runtime, which can lead to errors.
To avoid this problem, we have two options. One way is to pass different
parameters based on the operating system we're using. However, a more
direct approach is to specify that we're compiling for an embedded
system. This way, the linker won't attempt to link the C runtime environment,
ensuring a successful build without linker errors.
First add the target architecture. Open your terminal run the command
\begin{minted}{rust}
rustup target add thumbv6m-none-eabi
\end{minted}
This command uses rustup, the Rust toolchain installer, to add support
for compiling Rust code for the thumbv6m-none-eabi target, which is
a common architecture for ARM Cortex-M microcontrollers. You can also
choose alternative targets as long as the underlying environment doesn't
include an operating system.
Execute
\begin{minted}{rust}
cargo build --target=thumbv6m-none-eabi
\end{minted}
to compile your project for the thumbv6m-none-eabi target. This tells
Cargo, Rust's package manager and build system, to compile the project
for the specified architecture rather than the default target platform
that is your host machine.
\subsection{Output}
At this point, the program won't produce any output. If all the steps
proceed smoothly, it should compile successfully without reporting
any errors. For example:
\begin{minted}{rust}
cargo build --target=thumbv6m-none-eabi
Compiling chronos_labs v0.1.0 (/home/lucia/2023cse/project/chronos_labs)
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
\end{minted}
\section{Task 2 - Build Minimal Kernel }
\subsection{Introduction}
We'll use Rust to create a small 64-bit kernel for the x86 architecture
base on program we made for previous task. We will use the bootloader
tool to create a bootable disk image, allowing us to launch it using
QEMU.
See branch lab-1-2 for the code.
\subsection{Implementation}
\textbf{Step 1: Create a custom target specification file}
In the previous task, we referenced an embedded environment as our
compilation target. However, to build our custom operating system,
we need to write a custom target specification file. Create a chronos\_labs.json
file in the root directory, although you can choose any name for this
file. Create this file:
\begin{minted}{rust}
touch chronos_labs.json
\end{minted}
Here is the content of the file:
\begin{minted}{rust}
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
\end{minted}
You don't necessarily need to understand what each fields represents,
but here are a few of the parameters that are more unique compared
to other operating systems and might be worth understanding:
\begin{itemize}
\item \textquotedbl llvm-target\textquotedbl : \textquotedbl x86\_64-unknown-none\textquotedbl :
Specifies the target architecture for the compiler. Here, it's for
64-bit x86 architecture without a specific vendor or operating system.
\item \textquotedbl arch\textquotedbl : \textquotedbl x86\_64\textquotedbl :
The architecture of the target system, indicating a 64-bit processor.
\item \textquotedbl linker-flavor\textquotedbl : \textquotedbl ld.lld\textquotedbl{}
and \textquotedbl linker\textquotedbl : \textquotedbl rust-lld\textquotedbl :
Specify which linker to use, here it's cross-platform LLD linker included
with Rust.
\item \textquotedbl panic-strategy\textquotedbl : \textquotedbl abort\textquotedbl :
Determines how to handle panic situations. \textquotedbl abort\textquotedbl{}
means the program will immediately stop, without trying to unwind
the stack.
\item \textquotedbl disable-redzone\textquotedbl : true: Disables the
red zone, or sometimes it could lead to stack corruption.o
\item \textquotedbl features\textquotedbl : \textquotedbl -mmx,-sse,+soft-float\textquotedbl :
Specifies CPU features to enable or disable. Here, MMX and SSE are
disabled, while software-based floating-point calculations are enabled.
Disable of mmx and sse features means we disable the Single Instruction
Multiple Data (SIMD) instructions because it will cause interruption
too frequently. And enable soft-float that simulates all floating-point
operations using software functions that rely on regular integers
will solve the error by disable SIMD.
\end{itemize}
\textbf{Step 2: Create .cargo/config.toml}
In your project's root directory, create a folder named .cargo
\begin{minted}{rust}
mkdir .cargo
\end{minted}
Inside the .cargo folder, create a file named config.toml
\begin{minted}{rust}
cd .cargo && touch config.toml
\end{minted}
Open config.toml and paste the following contents:
\begin{minted}{rust}
[unstable]
build-std = ["core", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem"]
[build]
target = "chronos_labs.json" #replace with your file name
\end{minted}
Here are explanations for each lines:
\textbf{{[}unstable{]}}
build-std = {[}\textquotedbl core\textquotedbl , \textquotedbl compiler\_builtins\textquotedbl{]}:
Tells Cargo to compile essential Rust libraries core and compiler\_builtins
from scratch
build-std-features = {[}\textquotedbl compiler-builtins-mem\textquotedbl{]}:
Activates memory functions in compiler\_builtins
\textbf{{[}build{]} }
target = \textquotedbl chronos\_labs.json\textquotedbl : Points
to a custom target file to specify how to compile for a particular
setup.
\textbf{Step 3: Use bootimage}
Add bootimage to dependency, open Cargo.toml and add under {[}dependencies{]}
\begin{minted}{rust}
[dependencies]
bootloader = "0.9.23"
\end{minted}
Also add these in .cargo/config.toml
\begin{minted}{rust}
[target.'cfg(target_os = "none")']
runner = "bootimage runner"
\end{minted}
{[}target.'cfg(target\_os = \textquotedbl none\textquotedbl )'{]}
include \textquotedbl chronos\_labs.json\textquotedbl{} file, and
runner key defines command gets executed bootimage runner after the
project has been successfully compiled.
Now we can use cargo run to execute this project. The cargo run command
is a convenient tool used in Rust projects to compile and run the
application code in one step.
If you interested in what did bootimage tool do, see\ref{sec:What-did-bootimage}
\subsection{Output}
The output should be a blank QEMU window:
\includegraphics[width=0.6\paperwidth]{\string"/home/lucia/Pictures/Screenshots/Screenshot from 2024-01-28 22-55-22\string".png}
\section{Task 3 - Show something!}
\subsection{Introduction}
We use VGA text buffer to make output for this operating system. It
is because it's simple and straightforward to write to. VGA text mode
provides a direct way to display text on the screen by writing characters
and their attributes (like color) to a specific area of memory. More
inforamtion about it \ref{sec:VGA-text-buffer}
In the Windows operating system, encountering an error often results
in the appearance of a blue screen. Therefore, we will also attempt
to display a blue screen in our system.
\subsection{Implementation}
Open src/main.rs
\textbf{Step 1: Define Constants}
Add these constant
\begin{minted}{rust}
const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;
const BACKGROUND_COLOR: u16 = 0x1000; // blue background, black foreground
\end{minted}
\begin{itemize}
\item const BUFFER\_HEIGHT: usize = 25; Defines a constant named BUFFER\_HEIGHT
with a type of usize (an unsigned size type, which means it's a number
that can't be negative and its size varies based on the computer architecture).
The value 25 represents the number of text lines that the VGA text
buffer can display at one time.
\item const BUFFER\_WIDTH: usize = 80; Similar to the first line, this defines
a constant named BUFFER\_WIDTH, also of type usize. The value 80 represents
the number of characters that can fit on a single line of the VGA
text buffer.
\item const BACKGROUND\_COLOR: u16 = 0x1000; This line defines a constant
named BACKGROUND\_COLOR with a type of u16 (a 16-bit unsigned integer).
The value 0x1000 is a hexadecimal number that specifies the color
attributes for the text and background. It will be 000100000000 in
binary. Recover from \ref{sec:VGA-text-buffer}, we know that it sets
the background color to blue and the foreground (text) color to black.
\end{itemize}
See more about variables in Rust on \ref{sec:Variables}
\textbf{Step 2: Initializes Buffer}
Add this line inside \_start()
\begin{minted}{rust}
let vga_buffer = unsafe { core::slice::from_raw_parts_mut(0xb8000 as *mut u16, 2000) };
\end{minted}
\begin{itemize}
\item unsafe \{ ... \}: In Rust, unsafe blocks are used for performing unsafe
operations, such as direct hardware access or low-level memory operations.
Here, the unsafe block is used for operations related to hardware
interaction.
\item core::slice::from\_raw\_parts\_mut(0xb8000 as {*}mut u16, 2000): This
is a function call that creates a mutable slice
\end{itemize}
- 0xb8000 as {*}mut u16: This is a memory address conversion, casting
the hexadecimal address 0xb8000 as a mutable pointer to a u16 type.
0xb8000 is starting address of VGA buffer, and each element is a 16-bit
character/color combination.\\
- 2000: This is the length of the slice, indicating that the slice
contains 2000 u16 elements. That's the size of VGA buffer by 80{*}25
= 2000
\textbf{Step 3: Assign values}
Add a for loop below
\begin{minted}{rust}
for i in 0..(BUFFER_HEIGHT * BUFFER_WIDTH) {
vga_buffer[i] = BACKGROUND_COLOR;
}
\end{minted}
Sets each element of the vga\_buffer array to BACKGROUND\_COLOR. In
this case, leave character section empty.
\subsection{Output}
\includegraphics[width=0.6\paperwidth]{\string"/home/lucia/Pictures/Screenshots/Screenshot from 2024-01-29 13-53-17\string".png}
You may try to display other color than blue by yourself? You could
find the color code from \url{https://wiki.osdev.org/Printing_To_Screen}
\chapter{Lab 2 - VGA output}
\section{Expected Outcome}
In this lab, we'll implement support of safety output string, number
and support Rust\textquoteright s formatting macros instead of write
on buffer directly. In this process, we will use traits to implement
more functionality with less, cleaner, and more concise code. We will
establish an interface that ensures safety and simplicity by isolating
all unsafe operations within a dedicated module.
\section{Task 1 - Print text at a specified position using ASCII encoding}
\subsection{Introduction}
At the end of lab 1, we traversed the entire VGA buffer to output
a blue screen. Now we will continue to use slices to output specific
text at specified positions.
\subsection{Implementation}
\textbf{Step 1: Modify the BACKGROUND\_COLOR constant}
In the previous implementation, we used the u16 data type for assignment
because we didn't need to output specific characters. However, this
time we will only define the color part. If you forget how to define
it, you can refer to the documentation.\ref{sec:VGA-text-buffer}
Change constant name into COLOR
\begin{minted}{rust}
const COLOR: u8 = 0x04; // black background, red foreground
\end{minted}
You could change into different value to represent different color
as you wish!
\textbf{Step 2: Modify vga\_buffer}
We change pointer datatype from u16 to u8 because we want to assign
character and color seperately. Length change to 4000 so the end of
buffer remain unchanged.
\begin{minted}{rust}
let vga_buffer = unsafe { core::slice::from_raw_parts_mut(0xb8000 as *mut u8, 4000) };
\end{minted}
\textbf{Step 3: Print character}
Delete the for loop under buffer definition and change into
\begin{minted}{rust}
vga_buffer[0] = b'H';
vga_buffer[1] = COLOR;
vga_buffer[2] = b'e';
vga_buffer[3] = COLOR;
vga_buffer[4] = b'l';
vga_buffer[5] = COLOR;
vga_buffer[6] = b'l';
vga_buffer[7] = COLOR;
vga_buffer[8] = b'o';
vga_buffer[9] = COLOR;
\end{minted}
\begin{itemize}
\item b'H': It represents a byte literal(a single byte of data). In this
case, corresponds to the ASCII encoding of the uppercase letter 'H'.
\item COLOR: Color part of the character, represent red foreground and black
background.
\end{itemize}
Remember to keep
\begin{minted}{rust}
loop {}
\end{minted}
Don't delete it!
You would see the output like this if everything going well
\includegraphics[width=0.6\paperwidth]{\string"/home/lucia/Pictures/Screenshots/Screenshot from 2024-02-07 19-19-19\string".png}
\textbf{Step 4: Print somewhere else}
Remember that
\begin{minted}{rust}
const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;
\end{minted}
You can print words at the new line
\begin{minted}{rust}
// write "World" at the next line
vga_buffer[160] = b'W';
vga_buffer[161] = COLOR;
vga_buffer[162] = b'o';
vga_buffer[163] = COLOR;
vga_buffer[164] = b'r';
vga_buffer[165] = COLOR;
vga_buffer[166] = b'l';
vga_buffer[167] = COLOR;
vga_buffer[168] = b'd';
vga_buffer[169] = COLOR;
\end{minted}
Or at the end
\begin{minted}{rust}
// End
vga_buffer[3998] = b'!';
vga_buffer[3999] = COLOR;
\end{minted}
Now you can visually see the correspondence between memory addresses
and screen positions.
\subsection{Output}
The code may look cumbersome, but it's okay because it's just to demonstrate
the correspondence between the VGA buffer and the screen. We'll implement
the print! and println! functions in a more elegant way.
\includegraphics[width=0.6\paperwidth]{\string"/home/lucia/Pictures/Screenshots/Screenshot from 2024-02-07 18-47-50\string".png}
\section{Task 2 - Write byte}
\subsection{Introduction}
In this task, we will create a vga.rs file to handle VGA output specifically,
aiming to improve code readability. At the same time, we'll use a
more elegant approach to output single characters, recognize newline
characters, and handle situations where characters exceed the screen.
\subsection{Implementation}
\textbf{Step 1: Create src/vga.rs file}
Create a vga.rs file in the src folder, and copy the necessary declarations.
We will import this file as module to main. More about module?\ref{sec:Module}
\begin{minted}{rust}
const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;
const COLOR: u8 = 0x04; // black background, red foreground
\end{minted}
\textbf{Step 2: Create a new struct represent single VGA character}
We create VGAChar that contain both ASCII and color. And in Rust,
the ordering of fields in default structs is not defined, so we use
the repr(C) attribute to ensure that the struct's fields are laid
out in memory exactly in order.
\begin{minted}{rust}
#[repr(C)]
#[derive(Clone, Copy)]
struct VGAChar {
ascii: u8,
color: u8,
}
\end{minted}
More about struct see \ref{sec:Structs}
It also relate to the big endian and little endian that define in\textbf{
}specification file \ref{sec:Big-endian-and}
\begin{itemize}
\item \#{[}derive(Clone, Copy){]}: Generates code to implement the Clone
and Copy for that type, needed by volatile.
\end{itemize}
\textbf{Step 3: Struct for buffer}
We use the volatile library, which helps us prevent Rust's compiler
from optimizing out our write operations to the buffer.
Add support in Cargo.toml
\begin{minted}{rust}
[dependencies]
bootloader = "0.9.23"
volatile = "0.2.6"
\end{minted}
Add these lines under VGAChar
\begin{minted}{rust}
#[repr(transparent)]
struct Buffer {
chars: [[Volatile<VGAChar>; BUFFER_WIDTH]; BUFFER_HEIGHT], // 2D array
}
\end{minted}
We use a 2D array to represent rows and columns.
\begin{itemize}
\item \#{[}repr(transparent){]}: Buffer has the same memory layout as its
inner 2D array of Volatile<VGAChar> elements
\end{itemize}
\textbf{Step 4: Implement Writer}
\begin{minted}{rust}
pub struct Writer {
column_position: usize,
row_position: usize,
buffer: &'static mut Buffer,
}
\end{minted}
For line buffer: \&'static mut Buffer