Compare commits

..

86 Commits

Author SHA1 Message Date
Philipp Oppermann
bd3550ea87 Merge pull request #1410 from v4zha/edition-3
loading UEFI using ovmf_prebuilt=0.2.3 with ovmf_code and ovmf_vars
2025-05-09 15:51:53 +02:00
V4zha
ecb60ec326 Update index.md
change to_str().unwrap() to display() in format! args
2025-04-24 18:48:52 +05:30
v4zha
8a1267477a loading UEFI using ovmf_prebuilt=0.2.3 with ovmf_code and ovmf_vars 2025-04-24 16:58:59 +05:30
Philipp Oppermann
ce01059620 Fix typos
Fixes #1339
2024-08-26 07:56:55 +02:00
Philipp Oppermann
4d0c3ac188 Merge pull request #1333 from proudmuslim-dev/patch-5
Move import to sensible location in chapter 3
2024-07-25 22:57:03 +02:00
proudmuslim-dev
d565cd125b Move import to sensible location in chapter 3
It served no purpose in the previous code block and would only confuse the user
2024-07-04 19:15:21 +00:00
Philipp Oppermann
ca86085360 Merge pull request #1299 from spocino/patch-1
fix typo'd variable name in post 3 (doesn't compile)
2024-02-27 07:52:00 +01:00
Samuel Pocino
5f3d38884c fix typo'd variable name in post 3 (doesn't compile) 2024-02-24 20:32:28 -05:00
Philipp Oppermann
f557d1c698 Merge pull request #1276 from proudmuslim-dev/patch-4
Fix `embedded_graphics` code + typo in chapter 3
2024-02-04 17:09:00 +01:00
proudmuslim-dev
0c248d027e Fix embedded_graphics code + correct typo in chapter 3
Compiles now
2024-01-31 12:55:20 -08:00
Philipp Oppermann
2cf0675a2d Merge pull request #1269 from proudmuslim-dev/patch-3
Fix typos in code for `embedded_graphics` crate in chapter 3
2024-01-28 12:18:43 +01:00
Philipp Oppermann
916ad36e78 Merge pull request #1270 from lachsdachs/patch-1
fix a lil typo
2024-01-28 12:09:11 +01:00
lachsdachs
3c2e91fa4e fix a lil typo
sturcts -> structs
2024-01-27 21:23:35 +01:00
proudmuslim-dev
c9683a2cd9 Fix typos in code for embedded_graphics crate in chapter 3
This still won't compile on account of the fact that the `Point` type apparently doesn't implement `Into<(usize, usize)>`. Attempting to change the type just results in more issues
2024-01-27 00:21:18 +00:00
Philipp Oppermann
514736b1d8 Merge pull request #1265 from proudmuslim-dev/patch-1
Fix typo in chapter 2
2024-01-24 10:01:44 +01:00
Philipp Oppermann
647b509971 Merge pull request #1266 from proudmuslim-dev/patch-2
Fix formatting in chapter 2
2024-01-22 11:27:39 +01:00
proudmuslim-dev
1118350b16 Fix formatting in chapter 2 2024-01-21 22:00:19 +00:00
proudmuslim-dev
fb096a7484 Fix typo in chapter 2 2024-01-21 21:34:52 +00:00
Philipp Oppermann
8a41fd65bf Polish section about drawing blue pixels and squares 2023-12-29 18:57:01 +01:00
Philipp Oppermann
50802c8332 Continue work on framebuffer post 2023-12-28 20:12:07 +01:00
Philipp Oppermann
ba410f40ba Fix source path 2023-12-28 20:11:46 +01:00
Philipp Oppermann
a119d36cc9 Tweak heading style 2023-12-28 17:59:23 +01:00
Philipp Oppermann
9080e69a09 Improve headings 2023-12-28 17:59:13 +01:00
Philipp Oppermann
2676d69c31 Remove striped output example
Doesn't really add much value.
2023-07-09 11:28:40 +02:00
Philipp Oppermann
c31f3c2728 Start describing framebuffer bitmap layout 2023-07-09 11:26:34 +02:00
Philipp Oppermann
5799263124 Start working on 'Screen Output' post 2023-07-08 13:49:41 +02:00
Kenny Strawn
ded60de8d0 Submit rough draft of Edition-3-Post-3 (#1223) 2023-07-08 13:21:56 +02:00
Philipp Oppermann
171956adc8 Merge branch 'main' into edition-3 2023-07-08 13:21:06 +02:00
Philipp Oppermann
e2a4464835 Merge pull request #1230 from phil-opp/fix-category-id
Fix: Use correct category ID for comment threads of translated posts
2023-06-25 14:22:38 +02:00
Philipp Oppermann
f0fe3929ab Fix: Use correct category ID for comment threads of translated posts 2023-06-25 14:12:40 +02:00
Philipp Oppermann
c728cf8225 Merge pull request #1218 from twilfredo/wilfred/fixup_deprecated_fn
posts/06-double-faults/index: fixup deprecated fn
2023-06-02 09:22:55 +02:00
Philipp Oppermann
f38c11ae8e Merge pull request #1219 from Firenezz/main
Correcting typos and errors in French translation of Post 1
2023-06-02 09:21:53 +02:00
Philipp Oppermann
a1b195ede0 Merge pull request #1226 from SPuntte/fix-post-06-ub-panic
Fix panics caused by misaligned pointer dereferences in "Double Faults" & "Introduction to Paging"
2023-06-02 09:21:06 +02:00
Pontus Lundström
cfd31a977d Repeat previous for post-08 2023-05-31 17:14:43 +03:00
Pontus Lundström
06dd5edb3f Fix panic caused by misaligned pointer dereference 2023-05-20 23:12:33 +03:00
Demelari
685f55dd31 Keep "fils d'exécution" 2023-05-05 14:40:12 -04:00
Demelari
0b6b053c54 Swap "bien-sûr" for "bien sûr" and "évidemment" 2023-05-05 14:04:49 -04:00
Demelari
e0464fbd44 Fix and use "autoporté" instead of "autonome" 2023-05-05 13:58:55 -04:00
Demelari
5e88b86d1e Fix some typos 2023-05-05 13:32:23 -04:00
Wilfred Mallawa
e6b507e6d2 posts/06-double-faults/index: fixup deprecated fn
The code segment from the example uses the newer function whilst the
text segment refers to the old/deprecated `set_cs` fn. Let's fix that.

Signed-off-by: Wilfred Mallawa <vindulawilfred@gmail.com>
2023-05-04 14:55:40 +10:00
Philipp Oppermann
5681d3f0f7 Finish rest of post 2023-05-01 15:06:05 +02:00
Philipp Oppermann
68d0c946f4 Set default rustup profile 2023-04-30 17:16:11 +02:00
Philipp Oppermann
9baab55788 Add rust-src component 2023-04-30 17:14:24 +02:00
Philipp Oppermann
2bc74ce8f7 Write sections about creating bootable disk image 2023-04-30 17:09:37 +02:00
Philipp Oppermann
7ce9ae1caf Remove profile overrides again after compiling for custom target 2023-04-30 16:54:38 +02:00
Philipp Oppermann
646e5ba502 Add a short introduction to rustup 2023-04-30 16:53:45 +02:00
Philipp Oppermann
bb754eadba Link to stable Rust docs 2023-04-30 16:53:25 +02:00
Philipp Oppermann
f377050605 Tweak colors for note blocks 2023-04-30 16:52:47 +02:00
Philipp Oppermann
5baf50a5b4 Merge pull request #1217 from phil-opp/remove-alloc-error-handler-text
Update 'Heap Allocation' post to remove `alloc_error_handler`
2023-04-29 12:35:07 +02:00
Philipp Oppermann
5d63300512 Fix markdown syntax 2023-04-29 12:30:50 +02:00
Philipp Oppermann
739d9e1a3c Update 'Heap Allocation' post to remove alloc_error_handler
The unstable feature was removed. Allocation errors are reported as normal panics now.
2023-04-29 12:30:08 +02:00
Philipp Oppermann
8a1063df5f Merge pull request #1211 from GNITOAHC/main
fix(translation zh-TW): typo
2023-04-17 18:57:59 +02:00
GINTOAHC
d2c6b281c8 fix(translation zh-TW): typo 2023-04-17 18:14:39 +08:00
Philipp Oppermann
dd228c4bb1 Start revising second half of booting post 2023-04-09 21:13:49 +02:00
Philipp Oppermann
020f2d7e8e Highlight start address in rust-objdump output 2023-04-09 20:09:49 +02:00
Philipp Oppermann
c337650fb3 Tweak highlighting of bash snippets 2023-04-09 20:09:22 +02:00
Philipp Oppermann
7262e552ae Update fonts 2023-04-09 19:59:52 +02:00
Philipp Oppermann
3900ddeb2c Merge pull request #1210 from phil-opp/fix-links
Fix link syntax
2023-04-09 19:56:46 +02:00
Philipp Oppermann
a7f4647e73 Fix link syntax 2023-04-09 19:54:58 +02:00
Philipp Oppermann
a7cfc562e9 Fix: Add back title and description for translated sites 2023-04-09 19:49:35 +02:00
Philipp Oppermann
2d6fc6bd76 Merge branch 'main' into edition-3 2023-04-09 19:33:05 +02:00
Philipp Oppermann
5f5320f8e0 Merge pull request #1209 from phil-opp/update-zola
Update `zola` to `v0.17.2`
2023-04-09 19:32:43 +02:00
Philipp Oppermann
6f6e6698c4 Update CI to use zola v0.17.2 2023-04-09 19:27:46 +02:00
Philipp Oppermann
1d0e7950fd Make blog compatible with zola v0.17.2 2023-04-09 19:26:43 +02:00
Philipp Oppermann
33b6b6ebe8 Improve line highlighting: left marker instead of different background 2023-04-09 18:49:12 +02:00
Philipp Oppermann
94d87308f4 Fix typo 2023-04-09 13:52:58 +02:00
Philipp Oppermann
c81a2e11db Improve first half of post 2023-03-25 20:24:02 +01:00
Philipp Oppermann
3ff7f48257 Highlight changed lines in code examples + other improvements 2023-03-25 19:10:59 +01:00
Philipp Oppermann
dba81f6d1c Rewrite introduction 2023-03-25 18:30:19 +01:00
Philipp Oppermann
ae6f001eae Use new branch naming scheme for edition-3 posts 2023-03-25 17:36:33 +01:00
Philipp Oppermann
5c00dbcb60 Merge branch 'main' into edition-3 2023-03-25 16:28:01 +01:00
Philipp Oppermann
79be18bfb5 Merge edition-3 changes of PR #1187 2023-03-25 16:07:21 +01:00
Philipp Oppermann
dd4e872f82 Remove outdated paragraph with dead link in first edition 2023-03-25 15:49:47 +01:00
Philipp Oppermann
d8ad83528d Merge pull request #1190 from somebodyLi/main
fix(edition-2-posts-01-zh-CN): translate issue
2023-03-09 18:08:14 +01:00
Jie Wei
7c1b57a663 [Translation][Chinese] post-09 (edition-2) (#1189) 2023-02-26 12:29:42 +01:00
emanuele-em
50cbf4f01c Update index.md (#1193) 2023-02-23 17:23:15 +01:00
Ritchie
559b2a195d fix(edition-2-posts-01-zh-CN): translate issue 2023-02-16 19:04:49 +08:00
Philipp Oppermann
e56c635c13 Merge pull request #1188 from phil-opp/fix-broken-links
Fix broken links
2023-01-26 10:30:14 +01:00
Kenny Strawn
6cf6e32ee9 Fix broken links 2023-01-26 10:14:21 +01:00
Philipp Oppermann
96c8aaf89d Fix --edition inconsistency 2023-01-02 09:50:56 +01:00
Philipp Oppermann
9f0bd81a79 Build drafts on CI 2022-12-18 20:19:10 +01:00
Philipp Oppermann
55508e6800 Mark post as draft again 2022-12-18 20:17:13 +01:00
Philipp Oppermann
ffc6d54009 Improve formatting of extra content on main page 2022-12-18 20:14:19 +01:00
Philipp Oppermann
dfd0e6c5d8 Improve chapter description 2022-12-18 20:04:20 +01:00
Philipp Oppermann
6fe8365b93 Fix broken links caused by autoformatter 2022-12-18 20:02:19 +01:00
Philipp Oppermann
6d8075fe87 Rework minimal kernel post 2022-12-18 19:58:48 +01:00
47 changed files with 2676 additions and 1218 deletions

View File

@@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- name: 'Download Zola' - name: 'Download Zola'
run: curl -sL https://github.com/getzola/zola/releases/download/v0.16.1/zola-v0.16.1-x86_64-unknown-linux-gnu.tar.gz | tar zxv run: curl -sL https://github.com/getzola/zola/releases/download/v0.17.2/zola-v0.17.2-x86_64-unknown-linux-gnu.tar.gz | tar zxv
- name: 'Install Python Libraries' - name: 'Install Python Libraries'
run: python -m pip install --user -r requirements.txt run: python -m pip install --user -r requirements.txt
working-directory: "blog" working-directory: "blog"
@@ -104,3 +104,23 @@ jobs:
- name: "Push Changes" - name: "Push Changes"
run: "git push" run: "git push"
working-directory: "blog_os_deploy" working-directory: "blog_os_deploy"
drafts:
name: "Build Drafts"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: 'Download Zola'
run: curl -sL https://github.com/getzola/zola/releases/download/v0.16.1/zola-v0.16.1-x86_64-unknown-linux-gnu.tar.gz | tar zxv
- name: 'Install Python Libraries'
run: python -m pip install --user -r requirements.txt
working-directory: "blog"
- name: "Run before_build.py script"
run: python before_build.py
working-directory: "blog"
- name: "Build Site with drafts"
run: ../zola build --drafts
working-directory: "blog"

View File

@@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- name: "Download Zola" - name: "Download Zola"
run: curl -sL https://github.com/getzola/zola/releases/download/v0.16.1/zola-v0.16.1-x86_64-unknown-linux-gnu.tar.gz | tar zxv run: curl -sL https://github.com/getzola/zola/releases/download/v0.17.2/zola-v0.17.2-x86_64-unknown-linux-gnu.tar.gz | tar zxv
- name: "Run zola check" - name: "Run zola check"
run: ../zola check run: ../zola check

View File

@@ -1,4 +1,6 @@
base_url = "https://os.phil-opp.com" base_url = "https://os.phil-opp.com"
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
generate_feed = true generate_feed = true
feed_filename = "rss.xml" feed_filename = "rss.xml"
@@ -34,10 +36,7 @@ author = { name = "Philipp Oppermann" }
default_language = "en" default_language = "en"
languages = ["en", "zh-CN", "zh-TW", "fr", "ja", "fa", "ru", "ko"] languages = ["en", "zh-CN", "zh-TW", "fr", "ja", "fa", "ru", "ko"]
[languages.en] [translations]
title = "Writing an OS in Rust"
description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
[languages.en.translations]
lang_name = "English (original)" lang_name = "English (original)"
toc = "Table of Contents" toc = "Table of Contents"
all_posts = "« All Posts" all_posts = "« All Posts"

View File

@@ -422,7 +422,7 @@ extern "C" fn divide_by_zero_handler() -> ! {
``` ```
We register a single handler function for a [divide by zero error] \(index 0). Like the name says, this exception occurs when dividing a number by 0. Thus we have an easy way to test our new exception handler. We register a single handler function for a [divide by zero error] \(index 0). Like the name says, this exception occurs when dividing a number by 0. Thus we have an easy way to test our new exception handler.
[divide by zero error]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error [divide by zero error]: https://wiki.osdev.org/Exceptions#Division_Error
However, it doesn't work this way: However, it doesn't work this way:

View File

@@ -31,8 +31,6 @@ nightly
[rustup]: https://www.rustup.rs/ [rustup]: https://www.rustup.rs/
The code from this post (and all following) is [automatically tested](https://travis-ci.org/phil-opp/blog_os) every day and should always work for the newest nightly. If it doesn't, please [file an issue](https://github.com/phil-opp/blog_os/issues).
## Creating a Cargo project ## Creating a Cargo project
[Cargo] is Rust's excellent package manager. Normally you would call `cargo new` when you want to create a new project folder. We can't use it because our folder already exists, so we need to do it manually. Fortunately we only need to add a cargo configuration file named `Cargo.toml`: [Cargo] is Rust's excellent package manager. Normally you would call `cargo new` when you want to create a new project folder. We can't use it because our folder already exists, so we need to do it manually. Fortunately we only need to add a cargo configuration file named `Cargo.toml`:

View File

@@ -128,7 +128,7 @@ First Exception | Second Exception
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] [Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] [Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error [Divide-by-zero]: https://wiki.osdev.org/Exceptions#Division_Error
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS [Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present [Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault [Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault

View File

@@ -1,5 +1,5 @@
+++ +++
title = "Un binaire Rust autonome" title = "Un binaire Rust autoporté"
weight = 1 weight = 1
path = "fr/freestanding-rust-binary" path = "fr/freestanding-rust-binary"
date = 2018-02-10 date = 2018-02-10
@@ -29,7 +29,7 @@ Ce blog est développé sur [GitHub]. Si vous avez un problème ou une question,
## Introduction ## Introduction
Pour écrire un noyau de système d'exploitation, nous avons besoin d'un code qui ne dépend pas de fonctionnalités de système d'exploitation. Cela signifie que nous ne pouvons pas utiliser les fils d'exécution, les fichiers, la mémoire sur le tas, le réseau, les nombres aléatoires, la sortie standard ou tout autre fonctionnalité nécessitant une abstraction du système d'exploitation ou un matériel spécifique. Cela a du sens, étant donné que nous essayons d'écrire notre propre OS et nos propres pilotes. Pour écrire un noyau de système d'exploitation, nous avons besoin d'un code qui ne dépend pas de fonctionnalités de système d'exploitation. Cela signifie que nous ne pouvons pas utiliser les fils d'exécution, les fichiers, la mémoire sur le tas, le réseau, les nombres aléatoires, la sortie standard ou tout autre fonctionnalité nécessitant une abstraction du système d'exploitation ou un matériel spécifique. Cela a du sens, étant donné que nous essayons d'écrire notre propre OS et nos propres pilotes.
Cela signifie que nous ne pouvons pas utiliser la majeure partie de la [bibliothèque standard de Rust]. Il y a néanmoins beaucoup de fonctionnalités de Rust que nous _pouvons_ utiliser. Par exemple, nous pouvons utiliser les [iterators], les [closures], le [pattern matching], l'[option] et le [result], le [string formatting], et bien-sûr l'[ownership system]. Ces fonctionnalités permettent l'écriture d'un noyau d'une façon expressive et haut-niveau sans se soucier des [comportements indéfinis] ou de la [sécurité de la mémoire]. Cela signifie que nous ne pouvons pas utiliser la majeure partie de la [bibliothèque standard de Rust]. Il y a néanmoins beaucoup de fonctionnalités de Rust que nous _pouvons_ utiliser. Par exemple, nous pouvons utiliser les [iterators], les [closures], le [pattern matching], l'[option] et le [result], le [string formatting], et bien sûr l'[ownership system]. Ces fonctionnalités permettent l'écriture d'un noyau d'une façon expressive et haut-niveau sans se soucier des [comportements indéfinis] ou de la [sécurité de la mémoire].
[option]: https://doc.rust-lang.org/core/option/ [option]: https://doc.rust-lang.org/core/option/
[result]:https://doc.rust-lang.org/core/result/ [result]:https://doc.rust-lang.org/core/result/
@@ -47,18 +47,18 @@ Cet article décrit les étapes nécessaires pour créer un exécutable Rust aut
## Désactiver la Bibliothèque Standard ## Désactiver la Bibliothèque Standard
Par défaut, tous les crates Rust relient la [bibliothèque standard], qui dépend du système d'exploitation pour les fonctionnalités telles que les fils d'exécution, les fichiers ou le réseau. Elle dépend aussi de la bibliothèque standard de C `libc`, qui intéragit de près avec les services de l'OS. Comme notre plan est d'écrire un système d'exploitation, nous ne pouvons pas utiliser des bibliothèques dépendant de l'OS. Nous devons donc désactiver l'inclusion automatique de la bibliothèque standard en utilisant l'[attribut `no std`]. Par défaut, toutes les crates Rust sont liées à la bibliothèque standard, qui repose sur les fonctionnalités du système dexploitation telles que les fils d'exécution, les fichiers et la connectivité réseau. Elle est également liée à la bibliothèque standard C `libc`, qui interagit étroitement avec les services fournis par l'OS. Comme notre plan est d'écrire un système d'exploitation, nous ne pouvons pas utiliser des bibliothèques dépendant de l'OS. Nous devons donc désactiver l'inclusion automatique de la bibliothèque standard en utilisant l'[attribut `no std`].
[bibliothèque standard]: https://doc.rust-lang.org/std/ [bibliothèque standard]: https://doc.rust-lang.org/std/
[attribut `no std`]: https://doc.rust-lang.org/1.30.0/book/first-edition/using-rust-without-the-standard-library.html [attribut `no std`]: https://doc.rust-lang.org/1.30.0/book/first-edition/using-rust-without-the-standard-library.html
Nous commencons par créer un nouveau projet d'application cargo. La manière la plus simple de faire est avec la ligne de commande : Nous commençons par créer un nouveau projet d'application cargo. La manière la plus simple de faire est avec la ligne de commande :
``` ```
cargo new blog_os --bin --edition 2018 cargo new blog_os --bin --edition 2018
``` ```
J'ai nommé le projet `blog_os`, mais vous pouvez bien-sûr choisir le nom qu'il vous convient. Le flag `--bin` indique que nous voulons créer un exécutable (contrairement à une bibliothèque) et le flag `--edition 2018` indique que nous voulons utiliser l'[édition 2018] de Rust pour notre crate. Quand nous lançons la commande, cargo crée la structure de répertoire suivante pour nous : J'ai nommé le projet `blog_os`, mais vous pouvez évidemment choisir le nom qu'il vous convient. Le flag `--bin` indique que nous voulons créer un exécutable (contrairement à une bibliothèque) et le flag `--edition 2018` indique que nous voulons utiliser l'[édition 2018] de Rust pour notre crate. Quand nous lançons la commande, cargo crée la structure de répertoire suivante pour nous :
[édition 2018]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html [édition 2018]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html
@@ -75,7 +75,7 @@ Le fichier `Cargo.toml` contient la configuration de la crate, par exemple le no
### L'Attribut `no_std` ### L'Attribut `no_std`
Pour l'instant, notre crate relie la bilbiothèque standard implicitement. Désactivons cela en ajoutant l'[attribut `no std`] : Pour l'instant, notre crate relie la bibliothèque standard implicitement. Désactivons cela en ajoutant l'[attribut `no std`] :
```rust ```rust
// main.rs // main.rs

View File

@@ -236,7 +236,7 @@ rustup target add thumbv7em-none-eabihf
cargo build --target thumbv7em-none-eabihf cargo build --target thumbv7em-none-eabihf
``` ```
我们传递了 `--target` 参数,来为裸机目标系统**交叉编译**[cross compile](https://en.wikipedia.org/wiki/Cross_compiler))我们的程序。我们的目标并不包括操作系统,所以链接器不会试着链接 C 语言运行环境,因此构建过程成功完成,不会产生链接器错误。 我们传递了 `--target` 参数,来为裸机目标系统**交叉编译**[cross compile](https://en.wikipedia.org/wiki/Cross_compiler))我们的程序。我们的目标并不包括操作系统,所以链接器不会试着链接 C 语言运行环境,因此构建过程成功完成,不会产生链接器错误。
我们将使用这个方法编写自己的操作系统内核。我们不会编译到 `thumbv7em-none-eabihf`,而是使用描述 `x86_64` 环境的**自定义目标**[custom target](https://doc.rust-lang.org/rustc/targets/custom.html))。在下一篇文章中,我们将详细描述一些相关的细节。 我们将使用这个方法编写自己的操作系统内核。我们不会编译到 `thumbv7em-none-eabihf`,而是使用描述 `x86_64` 环境的**自定义目标**[custom target](https://doc.rust-lang.org/rustc/targets/custom.html))。在下一篇文章中,我们将详细描述一些相关的细节。
@@ -424,7 +424,7 @@ use core::panic::PanicInfo;
#[no_mangle] // 不重整函数名 #[no_mangle] // 不重整函数名
pub extern "C" fn _start() -> ! { pub extern "C" fn _start() -> ! {
// 因为编译器会寻找一个名为 `_start` 的函数,所以这个函数就是入口点 // 因为链接器会寻找一个名为 `_start` 的函数,所以这个函数就是入口点
// 默认命名为 `_start` // 默认命名为 `_start`
loop {} loop {}
} }

View File

@@ -383,7 +383,7 @@ error: linking with `cc` failed: exit code: 1
cargo rustc -- -C link-args="-e __start" cargo rustc -- -C link-args="-e __start"
``` ```
`-e` 表示口點的函式名稱,然後由於 macOS 上所有的函式都會加上前綴 `_`,我們需要設置入口點為 `__start` 而不是 `_start` `-e` 表示口點的函式名稱,然後由於 macOS 上所有的函式都會加上前綴 `_`,我們需要設置入口點為 `__start` 而不是 `_start`
接下來會出現另一個連結器錯誤: 接下來會出現另一個連結器錯誤:

View File

@@ -323,7 +323,7 @@ build-std-features = ["compiler-builtins-mem"]
در پشت صحنه، این پرچم [ویژگی `mem`] از کریت `compiler_builtins` را فعال می‌کند. اثرش این است که صفت `[no_mangle]#` بر روی [پیاده‌سازی `memcpy` و بقیه موارد] از کریت اعمال می‌شود، که آن‌ها در دسترس لینکر قرار می‌دهد. شایان ذکر است که این توابع در حال حاضر [بهینه نشده‌اند]، بنابراین ممکن است عملکرد آ‌ن‌ها در بهترین حالت نباشد، اما حداقل صحیح هستند. برای `x86_64` ، یک pull request باز برای [بهینه سازی این توابع با استفاده از دستورالعمل‌های خاص اسمبلی][memcpy rep movsb] وجود دارد. در پشت صحنه، این پرچم [ویژگی `mem`] از کریت `compiler_builtins` را فعال می‌کند. اثرش این است که صفت `[no_mangle]#` بر روی [پیاده‌سازی `memcpy` و بقیه موارد] از کریت اعمال می‌شود، که آن‌ها در دسترس لینکر قرار می‌دهد. شایان ذکر است که این توابع در حال حاضر [بهینه نشده‌اند]، بنابراین ممکن است عملکرد آ‌ن‌ها در بهترین حالت نباشد، اما حداقل صحیح هستند. برای `x86_64` ، یک pull request باز برای [بهینه سازی این توابع با استفاده از دستورالعمل‌های خاص اسمبلی][memcpy rep movsb] وجود دارد.
[ویژگی `mem`]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/Cargo.toml#L51-L52 [ویژگی `mem`]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/Cargo.toml#L51-L52
[پیاده‌سازی `memcpy` و بقیه موارد]: (https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/src/mem.rs#L12-L69) [پیاده‌سازی `memcpy` و بقیه موارد]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/src/mem.rs#L12-L69
[بهینه نشده‌اند]: https://github.com/rust-lang/compiler-builtins/issues/339 [بهینه نشده‌اند]: https://github.com/rust-lang/compiler-builtins/issues/339
[memcpy rep movsb]: https://github.com/rust-lang/compiler-builtins/pull/365 [memcpy rep movsb]: https://github.com/rust-lang/compiler-builtins/pull/365

View File

@@ -317,7 +317,7 @@ build-std-features = ["compiler-builtins-mem"]
このとき、裏で`compiler_builtins`クレートの[`mem`機能][`mem` feature]が有効化されています。これにより、このクレートの[`memcpy`などの実装][`memcpy` etc. implementations]に`#[no_mangle]`アトリビュートが適用され、リンカがこれらを利用できるようになっています。 このとき、裏で`compiler_builtins`クレートの[`mem`機能][`mem` feature]が有効化されています。これにより、このクレートの[`memcpy`などの実装][`memcpy` etc. implementations]に`#[no_mangle]`アトリビュートが適用され、リンカがこれらを利用できるようになっています。
[`mem` feature]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/Cargo.toml#L51-L52 [`mem` feature]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/Cargo.toml#L51-L52
[`memcpy` etc. implementations]: (https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/src/mem.rs#L12-L69) [`memcpy` etc. implementations]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/src/mem.rs#L12-L69
この変更をもって、私達のカーネルはコンパイラに必要とされているすべての関数の有効な実装を手に入れたので、コードがもっと複雑になっても変わらずコンパイルできるでしょう。 この変更をもって、私達のカーネルはコンパイラに必要とされているすべての関数の有効な実装を手に入れたので、コードがもっと複雑になっても変わらずコンパイルできるでしょう。

View File

@@ -99,7 +99,7 @@ UEFI 표준으로 동작하는 최신 기기들도 가상 BIOS를 지원하기
지난 포스트에서 우리는 `cargo`를 통해 freestanding 실행파일을 만들었었는데, 호스트 시스템의 운영체제에 따라 프로그램 실행 시작 지점의 이름 및 컴파일 인자들을 다르게 설정해야 했습니다. 이것은 `cargo`가 기본적으로 _호스트 시스템_ (여러 분이 실행 중인 컴퓨터 시스템) 을 목표로 빌드하기 때문이었습니다. 우리의 커널은 다른 운영체제 (예를 들어 Windows) 위에서 실행될 것이 아니기에, 호스트 시스템에 설정 값을 맞추는 대신에 우리가 명확히 정의한 _목표 시스템 (target system)_ 을 목표로 컴파일할 것입니다. 지난 포스트에서 우리는 `cargo`를 통해 freestanding 실행파일을 만들었었는데, 호스트 시스템의 운영체제에 따라 프로그램 실행 시작 지점의 이름 및 컴파일 인자들을 다르게 설정해야 했습니다. 이것은 `cargo`가 기본적으로 _호스트 시스템_ (여러 분이 실행 중인 컴퓨터 시스템) 을 목표로 빌드하기 때문이었습니다. 우리의 커널은 다른 운영체제 (예를 들어 Windows) 위에서 실행될 것이 아니기에, 호스트 시스템에 설정 값을 맞추는 대신에 우리가 명확히 정의한 _목표 시스템 (target system)_ 을 목표로 컴파일할 것입니다.
### Rust Nightly 설치하기 {#installing-rust-nightly} ### Rust Nightly 설치하기 {#installing-rust-nightly}
Rust는 _stable_, _beta_ 그리고 _nightly_ 이렇게 세 가지의 채널을 통해 배포됩니다. Rust Book에 [세 채널들 간의 차이에 대해 잘 정리한 챕터]((https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#choo-choo-release-channels-and-riding-the-trains))가 있습니다. 운영체제를 빌드하기 위해서는 _nightly_ 채널에서만 제공하는 실험적인 기능들을 이용해야 하기에 _nightly_ 버전의 Rust를 설치하셔야 합니다. Rust는 _stable_, _beta_ 그리고 _nightly_ 이렇게 세 가지의 채널을 통해 배포됩니다. Rust Book에 [세 채널들 간의 차이에 대해 잘 정리한 챕터](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#choo-choo-release-channels-and-riding-the-trains)가 있습니다. 운영체제를 빌드하기 위해서는 _nightly_ 채널에서만 제공하는 실험적인 기능들을 이용해야 하기에 _nightly_ 버전의 Rust를 설치하셔야 합니다.
여러 버전의 Rust 언어 설치 파일들을 관리할 때 [rustup]을 사용하는 것을 강력 추천합니다. rustup을 통해 nightly, beta 그리고 stable 컴파일러들을 모두 설치하고 업데이트할 수 있습니다. `rustup override set nightly` 명령어를 통해 현재 디렉토리에서 항상 nightly 버전의 Rust를 사용하도록 설정할 수 있습니다. 여러 버전의 Rust 언어 설치 파일들을 관리할 때 [rustup]을 사용하는 것을 강력 추천합니다. rustup을 통해 nightly, beta 그리고 stable 컴파일러들을 모두 설치하고 업데이트할 수 있습니다. `rustup override set nightly` 명령어를 통해 현재 디렉토리에서 항상 nightly 버전의 Rust를 사용하도록 설정할 수 있습니다.
`rust-toolchain`이라는 파일을 프로젝트 루트 디렉토리에 만들고 이 파일에 `nightly`라는 텍스트를 적어 놓아도 같은 효과를 볼 수 있습니다. `rustc --version` 명령어를 통해 현재 nightly 버전이 설치되어 있는지 확인할 수 있습니다 (출력되는 버전 넘버가 `-nightly`라는 텍스트로 끝나야 합니다). `rust-toolchain`이라는 파일을 프로젝트 루트 디렉토리에 만들고 이 파일에 `nightly`라는 텍스트를 적어 놓아도 같은 효과를 볼 수 있습니다. `rustc --version` 명령어를 통해 현재 nightly 버전이 설치되어 있는지 확인할 수 있습니다 (출력되는 버전 넘버가 `-nightly`라는 텍스트로 끝나야 합니다).

View File

@@ -890,7 +890,7 @@ fn test_println() {
標準ライブラリのテストフレームワークは、[`#[should_panic]`属性][should_panic]をサポートしています。これを使うと、失敗しなければならないテストを作ることができます。これは、例えば、関数が無効な引数を渡されたときに失敗することを確かめる場合などに便利です。残念なことに、この機能は標準ライブラリのサポートを必要とするため、`#[no_std]`クレートではこの属性はサポートされていません。 標準ライブラリのテストフレームワークは、[`#[should_panic]`属性][should_panic]をサポートしています。これを使うと、失敗しなければならないテストを作ることができます。これは、例えば、関数が無効な引数を渡されたときに失敗することを確かめる場合などに便利です。残念なことに、この機能は標準ライブラリのサポートを必要とするため、`#[no_std]`クレートではこの属性はサポートされていません。
[should_panic]: https://doc.rust-jp.rs/rust-by-example-ja/testing/unit_testing.html#testing-panics [should_panic]: https://doc.rust-jp.rs/rust-by-example-ja/testing/unit_testing.html#パニックをテストする
`#[should_panic]`属性は使えませんが、パニックハンドラから成功のエラーコードで終了するような結合テストを作れば、似たような動きをさせることはできます。そのようなテストを`should_panic`という名前で作ってみましょう: `#[should_panic]`属性は使えませんが、パニックハンドラから成功のエラーコードで終了するような結合テストを作れば、似たような動きをさせることはできます。そのようなテストを`should_panic`という名前で作ってみましょう:

View File

@@ -49,7 +49,7 @@ pub extern "C" fn _start() -> ! {
// trigger a page fault // trigger a page fault
unsafe { unsafe {
*(0xdeadbeef as *mut u64) = 42; *(0xdeadbeef as *mut u8) = 42;
}; };
// as before // as before
@@ -138,7 +138,7 @@ extern "x86-interrupt" fn double_fault_handler(
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] [Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] [Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error [Divide-by-zero]: https://wiki.osdev.org/Exceptions#Division_Error
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS [Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present [Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault [Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault

View File

@@ -45,7 +45,7 @@ pub extern "C" fn _start() -> ! {
// ページフォルトを起こす // ページフォルトを起こす
unsafe { unsafe {
*(0xdeadbeef as *mut u64) = 42; *(0xdeadbeef as *mut u8) = 42;
}; };
// 前回同様 // 前回同様
@@ -132,7 +132,7 @@ CPUはダブルフォルトハンドラを呼べるようになったので、
[ゼロ除算],<br>[無効TSS],<br>[セグメント不在],<br>[スタックセグメントフォルト],<br>[一般保護違反] | [無効TSS],<br>[セグメント不在],<br>[スタックセグメントフォルト],<br>[一般保護違反] [ゼロ除算],<br>[無効TSS],<br>[セグメント不在],<br>[スタックセグメントフォルト],<br>[一般保護違反] | [無効TSS],<br>[セグメント不在],<br>[スタックセグメントフォルト],<br>[一般保護違反]
[ページフォルト] | [ページフォルト],<br>[無効TSS],<br>[セグメント不在],<br>[スタックセグメントフォルト],<br>[一般保護違反] [ページフォルト] | [ページフォルト],<br>[無効TSS],<br>[セグメント不在],<br>[スタックセグメントフォルト],<br>[一般保護違反]
[ゼロ除算]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error [ゼロ除算]: https://wiki.osdev.org/Exceptions#Division_Error
[無効TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS [無効TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
[セグメント不在]: https://wiki.osdev.org/Exceptions#Segment_Not_Present [セグメント不在]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
[スタックセグメントフォルト]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault [スタックセグメントフォルト]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault

View File

@@ -48,7 +48,7 @@ pub extern "C" fn _start() -> ! {
// 페이지 폴트 일으키기 // 페이지 폴트 일으키기
unsafe { unsafe {
*(0xdeadbeef as *mut u64) = 42; *(0xdeadbeef as *mut u8) = 42;
}; };
// 이전과 동일 // 이전과 동일
@@ -135,7 +135,7 @@ _“예외 처리 함수를 호출하는 것에 실패했을 때”_ 라는 게
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] [Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] [Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error [Divide-by-zero]: https://wiki.osdev.org/Exceptions#Division_Error
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS [Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present [Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault [Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault

View File

@@ -42,7 +42,7 @@ pub extern "C" fn _start() -> ! {
// trigger a page fault // trigger a page fault
unsafe { unsafe {
*(0xdeadbeef as *mut u64) = 42; *(0xdeadbeef as *mut u8) = 42;
}; };
// as before // as before
@@ -129,7 +129,7 @@ First Exception | Second Exception
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] [Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] [Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error [Divide-by-zero]: https://wiki.osdev.org/Exceptions#Division_Error
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS [Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present [Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault [Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault
@@ -374,9 +374,9 @@ pub fn init() {
} }
``` ```
We reload the code segment register using [`set_cs`] and load the TSS using [`load_tss`]. The functions are marked as `unsafe`, so we need an `unsafe` block to invoke them. The reason is that it might be possible to break memory safety by loading invalid selectors. We reload the code segment register using [`CS::set_reg`] and load the TSS using [`load_tss`]. The functions are marked as `unsafe`, so we need an `unsafe` block to invoke them. The reason is that it might be possible to break memory safety by loading invalid selectors.
[`set_cs`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/segmentation/fn.set_cs.html [`CS::set_reg`]: https://docs.rs/x86_64/0.14.5/x86_64/instructions/segmentation/struct.CS.html#method.set_reg
[`load_tss`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/tables/fn.load_tss.html [`load_tss`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/tables/fn.load_tss.html
Now that we have loaded a valid TSS and interrupt stack table, we can set the stack index for our double fault handler in the IDT: Now that we have loaded a valid TSS and interrupt stack table, we can set the stack index for our double fault handler in the IDT:

View File

@@ -47,7 +47,7 @@ pub extern "C" fn _start() -> ! {
// trigger a page fault // trigger a page fault
unsafe { unsafe {
*(0xdeadbeef as *mut u64) = 42; *(0xdeadbeef as *mut u8) = 42;
}; };
// as before // as before
@@ -137,7 +137,7 @@ extern "x86-interrupt" fn double_fault_handler(
| [Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | | [Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] |
| [Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | | [Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] |
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error [Divide-by-zero]: https://wiki.osdev.org/Exceptions#Division_Error
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS [Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present [Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault [Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault

View File

@@ -322,7 +322,7 @@ pub extern "C" fn _start() -> ! {
blog_os::init(); blog_os::init();
// new // new
let ptr = 0xdeadbeaf as *mut u32; let ptr = 0xdeadbeaf as *mut u8;
unsafe { *ptr = 42; } unsafe { *ptr = 42; }
// as before // as before
@@ -347,7 +347,7 @@ pub extern "C" fn _start() -> ! {
```rust ```rust
// Note: The actual address might be different for you. Use the address that // Note: The actual address might be different for you. Use the address that
// your page fault handler reports. // your page fault handler reports.
let ptr = 0x2031b2 as *mut u32; let ptr = 0x2031b2 as *mut u8;
// read from a code page // read from a code page
unsafe { let x = *ptr; } unsafe { let x = *ptr; }

View File

@@ -329,7 +329,7 @@ pub extern "C" fn _start() -> ! {
blog_os::init(); blog_os::init();
// ここを追加 // ここを追加
let ptr = 0xdeadbeaf as *mut u32; let ptr = 0xdeadbeaf as *mut u8;
unsafe { *ptr = 42; } unsafe { *ptr = 42; }
// ここはこれまでと同じ // ここはこれまでと同じ
@@ -354,7 +354,7 @@ pub extern "C" fn _start() -> ! {
```rust ```rust
// 注意:実際のアドレスは個々人で違うかもしれません。 // 注意:実際のアドレスは個々人で違うかもしれません。
// あなたのページフォルトハンドラが報告した値を使ってください。 // あなたのページフォルトハンドラが報告した値を使ってください。
let ptr = 0x2031b2 as *mut u32; let ptr = 0x2031b2 as *mut u8;
// コードページから読み込む // コードページから読み込む
unsafe { let x = *ptr; } unsafe { let x = *ptr; }

View File

@@ -316,7 +316,7 @@ pub extern "C" fn _start() -> ! {
blog_os::init(); blog_os::init();
// new // new
let ptr = 0xdeadbeaf as *mut u32; let ptr = 0xdeadbeaf as *mut u8;
unsafe { *ptr = 42; } unsafe { *ptr = 42; }
// as before // as before
@@ -341,7 +341,7 @@ We see that the current instruction pointer is `0x2031b2`, so we know that this
```rust ```rust
// Note: The actual address might be different for you. Use the address that // Note: The actual address might be different for you. Use the address that
// your page fault handler reports. // your page fault handler reports.
let ptr = 0x2031b2 as *mut u32; let ptr = 0x2031b2 as *mut u8;
// read from a code page // read from a code page
unsafe { let x = *ptr; } unsafe { let x = *ptr; }

View File

@@ -325,7 +325,7 @@ pub extern "C" fn _start() -> ! {
blog_os::init(); blog_os::init();
// new // new
let ptr = 0xdeadbeaf as *mut u32; let ptr = 0xdeadbeaf as *mut u8;
unsafe { *ptr = 42; } unsafe { *ptr = 42; }
// as before // as before
@@ -350,7 +350,7 @@ pub extern "C" fn _start() -> ! {
```rust ```rust
// Note: The actual address might be different for you. Use the address that // Note: The actual address might be different for you. Use the address that
// your page fault handler reports. // your page fault handler reports.
let ptr = 0x2031b2 as *mut u32; let ptr = 0x2031b2 as *mut u8;
// read from a code page // read from a code page
unsafe { let x = *ptr; } unsafe { let x = *ptr; }

File diff suppressed because it is too large Load Diff

View File

@@ -214,7 +214,7 @@ Since we are compiling for a custom target, we can't use the precompiled version
[unstable] [unstable]
build-std = ["core", "compiler_builtins", "alloc"] build-std = ["core", "compiler_builtins", "alloc"]
```` ```
Now the compiler will recompile and include the `alloc` crate in our kernel. Now the compiler will recompile and include the `alloc` crate in our kernel.
@@ -223,18 +223,12 @@ The reason that the `alloc` crate is disabled by default in `#[no_std]` crates i
``` ```
error: no global memory allocator found but one is required; link to std or add error: no global memory allocator found but one is required; link to std or add
#[global_allocator] to a static item that implements the GlobalAlloc trait. #[global_allocator] to a static item that implements the GlobalAlloc trait.
error: `#[alloc_error_handler]` function required, but not found
``` ```
The first error occurs because the `alloc` crate requires a heap allocator, which is an object that provides the `allocate` and `deallocate` functions. In Rust, heap allocators are described by the [`GlobalAlloc`] trait, which is mentioned in the error message. To set the heap allocator for the crate, the `#[global_allocator]` attribute must be applied to a `static` variable that implements the `GlobalAlloc` trait. The error occurs because the `alloc` crate requires a heap allocator, which is an object that provides the `allocate` and `deallocate` functions. In Rust, heap allocators are described by the [`GlobalAlloc`] trait, which is mentioned in the error message. To set the heap allocator for the crate, the `#[global_allocator]` attribute must be applied to a `static` variable that implements the `GlobalAlloc` trait.
The second error occurs because calls to `allocate` can fail, most commonly when there is no more memory available. Our program must be able to react to this case, which is what the `#[alloc_error_handler]` function is for.
[`GlobalAlloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html [`GlobalAlloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html
We will describe these traits and attributes in detail in the following sections.
### The `GlobalAlloc` Trait ### The `GlobalAlloc` Trait
The [`GlobalAlloc`] trait defines the functions that a heap allocator must provide. The trait is special because it is almost never used directly by the programmer. Instead, the compiler will automatically insert the appropriate calls to the trait methods when using the allocation and collection types of `alloc`. The [`GlobalAlloc`] trait defines the functions that a heap allocator must provide. The trait is special because it is almost never used directly by the programmer. Instead, the compiler will automatically insert the appropriate calls to the trait methods when using the allocation and collection types of `alloc`.
@@ -329,32 +323,7 @@ static ALLOCATOR: Dummy = Dummy;
Since the `Dummy` allocator is a [zero-sized type], we don't need to specify any fields in the initialization expression. Since the `Dummy` allocator is a [zero-sized type], we don't need to specify any fields in the initialization expression.
When we now try to compile it, the first error should be gone. Let's fix the remaining second error: With this static, the compilation errors should be fixed. Now we can use the allocation and collection types of `alloc`. For example, we can use a [`Box`] to allocate a value on the heap:
```
error: `#[alloc_error_handler]` function required, but not found
```
### The `#[alloc_error_handler]` Attribute
As we learned when discussing the `GlobalAlloc` trait, the `alloc` function can signal an allocation error by returning a null pointer. The question is: how should the Rust runtime react to such an allocation failure? This is where the `#[alloc_error_handler]` attribute comes in. It specifies a function that is called when an allocation error occurs, similar to how our panic handler is called when a panic occurs.
Let's add such a function to fix the compilation error:
```rust
// in src/lib.rs
#![feature(alloc_error_handler)] // at the top of the file
#[alloc_error_handler]
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
panic!("allocation error: {:?}", layout)
}
```
The `alloc_error_handler` function is still unstable, so we need a feature gate to enable it. The function receives a single argument: the `Layout` instance that was passed to `alloc` when the allocation failure occurred. There's nothing we can do to resolve the failure, so we just panic with a message that contains the `Layout` instance.
With this function, the compilation errors should be fixed. Now we can use the allocation and collection types of `alloc`. For example, we can use a [`Box`] to allocate a value on the heap:
[`Box`]: https://doc.rust-lang.org/alloc/boxed/struct.Box.html [`Box`]: https://doc.rust-lang.org/alloc/boxed/struct.Box.html
@@ -380,11 +349,11 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
Note that we need to specify the `extern crate alloc` statement in our `main.rs` too. This is required because the `lib.rs` and `main.rs` parts are treated as separate crates. However, we don't need to create another `#[global_allocator]` static because the global allocator applies to all crates in the project. In fact, specifying an additional allocator in another crate would be an error. Note that we need to specify the `extern crate alloc` statement in our `main.rs` too. This is required because the `lib.rs` and `main.rs` parts are treated as separate crates. However, we don't need to create another `#[global_allocator]` static because the global allocator applies to all crates in the project. In fact, specifying an additional allocator in another crate would be an error.
When we run the above code, we see that our `alloc_error_handler` function is called: When we run the above code, we see that a panic occurs:
![QEMU printing "panicked at `allocation error: Layout { size_: 4, align_: 4 }, src/lib.rs:89:5"](qemu-dummy-output.png) ![QEMU printing "panicked at `allocation error: Layout { size_: 4, align_: 4 }, src/lib.rs:89:5"](qemu-dummy-output.png)
The error handler is called because the `Box::new` function implicitly calls the `alloc` function of the global allocator. Our dummy allocator always returns a null pointer, so every allocation fails. To fix this, we need to create an allocator that actually returns usable memory. The panic occurs because the `Box::new` function implicitly calls the `alloc` function of the global allocator. Our dummy allocator always returns a null pointer, so every allocation fails. To fix this, we need to create an allocator that actually returns usable memory.
## Creating a Kernel Heap ## Creating a Kernel Heap

View File

@@ -58,7 +58,7 @@ translation_contributors = ["asami-kawasaki", "Foo-x"]
[コールスタック]: https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%BC%E3%83%AB%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF [コールスタック]: https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%BC%E3%83%AB%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF
[コンテキスト・スイッチ (context switch)]: https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%B9%E3%82%A4%E3%83%83%E3%83%81 [コンテキスト・スイッチ (context switch)]: https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%B9%E3%82%A4%E3%83%83%E3%83%81
コールスタックは非常に大きくなる可能性があるため、OSは通常、各タスクのスイッチでコールスタックの内容をバックアップする代わりに、各タスクに個別のコールスタックを設定します。このような独立したスタックを持つタスクは、[_thread of execution_](略して**スレッド**)と呼ばれます。タスクごとに独立したスタックを使用することで、コンテキスト・スイッチの際に保存する必要があるのはレジスタの内容だけになります(プログラム・カウンタとスタック・ポインタを含む)。この方法を取ることで、コンテキスト・スイッチの性能上のオーバーヘッドが最小限になります。これは、コンテキスト・スイッチが1秒間に100回も行われることがあるため、非常に重要なことです。 コールスタックは非常に大きくなる可能性があるため、OSは通常、各タスクのスイッチでコールスタックの内容をバックアップする代わりに、各タスクに個別のコールスタックを設定します。このような独立したスタックを持つタスクは、[略して**スレッド**][_thread of execution_]と呼ばれます。タスクごとに独立したスタックを使用することで、コンテキスト・スイッチの際に保存する必要があるのはレジスタの内容だけになります(プログラム・カウンタとスタック・ポインタを含む)。この方法を取ることで、コンテキスト・スイッチの性能上のオーバーヘッドが最小限になります。これは、コンテキスト・スイッチが1秒間に100回も行われることがあるため、非常に重要なことです。
[_thread of execution_]: https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF) [_thread of execution_]: https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF)

View File

@@ -3,4 +3,7 @@ title = "Bare Bones"
render = false render = false
+++ +++
In this first chapter, we explain how to create an operating system for the `x86_64` architecture step for step. Starting from scratch, we first create a minimal Rust executable that doesn't depend on the standard library. We then turn it into a bootable OS kernel by combining it with a bootloader. The resulting disk image can then be launched in the [QEMU](https://www.qemu.org/) emulator or booted on a real machine. In this first chapter, we explain how to create an operating system for the `x86_64` architecture step by step.
Starting from scratch, we first create a minimal Rust executable that doesn't depend on the standard library.
We then turn it into a bootable OS kernel by combining it with a bootloader.
The resulting disk image can then be launched in the [QEMU](https://www.qemu.org/) emulator or booted on a real machine.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -12,14 +12,480 @@ icon = '''<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi
</svg>''' </svg>'''
+++ +++
Dolores qui incidunt sit fugiat amet consequatur. Qui ab vel et molestias ex nemo corporis consequatur. Quia consequuntur itaque sequi quia autem. Maxime vel quis maxime at. Tenetur eveniet velit dolor quidem temporibus tenetur. In this post we focus on the [framebuffer], a special memory region that controls the screen output.
Using an [external crate], we will create functions for writing individual pixels, lines, and various shapes.
In the the second half of this post, we will explore text rendering and learn how to print the obligatory _["Hello, World!"]_.
[framebuffer]: https://en.wikipedia.org/wiki/Framebuffer
[external crate]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
["Hello, World!"]: https://en.wikipedia.org/wiki/Hello_world
<!-- more --> <!-- more -->
Molestiae quidem ipsa nihil laboriosam sapiente laudantium quia. Praesentium et repudiandae minima voluptas et. Repellendus voluptatem distinctio enim et alias distinctio recusandae quos. Dolores ex eum culpa quo sunt sint voluptate voluptates. Facere unde sequi quo ea vel nihil. Rem deleniti repellat rem molestias This blog is openly developed on [GitHub].
If you have any problems or questions, please open an issue there.
You can also leave comments [at the bottom].
The complete source code for this post can be found in the [`post-3.3`][post branch] branch.
[GitHub]: https://github.com/phil-opp/blog_os
[at the bottom]: #comments
<!-- fix for zola anchor checker (target is in template): <a id="comments"> -->
[post branch]: https://github.com/phil-opp/blog_os/tree/post-3.3
<!-- toc --> <!-- toc -->
Molestiae quidem ipsa nihil laboriosam sapiente laudantium quia. Praesentium et repudiandae minima voluptas et. Repellendus voluptatem distinctio enim et alias distinctio recusandae quos. Dolores ex eum culpa quo sunt sint voluptate voluptates. Facere unde sequi quo ea vel nihil. Rem deleniti repellat rem molestias
## Bitmap Images
In the [previous post], we learned how to make our minimal kernel bootable.
Using the [`BootInfo`] provided by the bootloader, we were able to access a special memory region called the _[framebuffer]_, which controls the screen output.
We wrote some example code to display a gray background:
[previous post]: @/edition-3/posts/02-booting/index.md
[`BootInfo`]: https://docs.rs/bootloader_api/0.11/bootloader_api/info/struct.BootInfo.html
```rust
// in kernel/src/main.rs
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
if let Some(framebuffer) = boot_info.framebuffer.as_mut() {
for byte in framebuffer.buffer_mut() {
*byte = 0x90;
}
}
loop {}
}
```
The reason that the above code affects the screen output is because the graphics card interprets the framebuffer memory as a [bitmap] image.
A bitmap describes an image through a predefined number of bytes per pixel.
The pixels are laid out line by line, typically starting at the top.
[bitmap]: https://en.wikipedia.org/wiki/Bitmap
[RGB]: https://en.wikipedia.org/wiki/Rgb
For example, the pixels of an image with width 10 and height 3 would be typically stored in this order:
<table style = "width: fit-content;"><tbody>
<tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr>
<tr><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td></tr>
<tr><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td></tr>
</tbody></table>
So top left pixel is stored at offset 0 in the bitmap array.
The pixel on its right is at pixel offset 1.
The first pixel of the next line starts at pixel offset `line_length`, which is 10 in this case.
The last line starts at pixel offset 20, which is `line_length * 2`.
### Padding
Depending on the hardware and GPU firmware, it is often more efficient to make lines start at well-aligned offsets.
Because of this, there is often some additional padding at the end of each line.
So the actual memory layout of the 10x3 example image might look like this, with the padding marked as yellow:
<table style = "width: fit-content;"><tbody>
<tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td style="background-color:yellow;">10</td><td style="background-color:yellow;">11</td></tr>
<tr><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td><td style="background-color:yellow;">22</td><td style="background-color:yellow;">23</td></tr>
<tr><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td><td>31</td><td>32</td><td>33</td><td style="background-color:yellow;">34</td><td style="background-color:yellow;">35</td></tr>
</tbody></table>
So now the second line starts at pixel offset 12.
The two pixels at the end of each line are considered as padding and ignored.
So if we want to set the first pixel of the second line, we need to be aware of the additional padding and set the pixel at offset 12 instead of offset 10.
The line length plus the padding bytes is typically called the _stride_ or _pitch_ of the buffer.
In the example above, the stride is 12 and the line length is 10.
Since the amount of padding depends on the hardware, the stride is only known at runtime.
The `bootloader` crate queries the framebuffer parameters from the UEFI or BIOS firmware and reports them as part of the `BootInfo`.
It provides the stride of the framebuffer, among other parameters, in form of a [`FrameBufferInfo`] struct that can be created using the [`FrameBuffer::info`] method.
[`FrameBufferInfo`]: https://docs.rs/bootloader_api/0.11/bootloader_api/info/struct.FrameBufferInfo.html
[`FrameBuffer::info`]: https://docs.rs/bootloader_api/0.11/bootloader_api/info/struct.FrameBuffer.html#method.info
### Color formats
The [`FrameBufferInfo`] also specifies the [`PixelFormat`] of the framebuffer, which also depends on the underlying hardware.
Using this information, we can set pixels to different colors.
For example, the [`PixelFormat::Rgb`] variant specifies that each pixel is represented in the [RGB color space], which stores the red, green, and blue parts of the pixel as separate bytes.
In this model, the color red would be represented as the three bytes `[255, 0, 0]`, or `0xff0000` in [hexadecimal representation].
The color yellow is represented the addition of red and green, which results in `[255, 255, 0]` (or `0xffff00` in hexadecimal representation).
[`PixelFormat`]: https://docs.rs/bootloader_api/0.11/bootloader_api/info/enum.PixelFormat.html
[`PixelFormat::Rgb`]: https://docs.rs/bootloader_api/0.11/bootloader_api/info/enum.PixelFormat.html#variant.Rgb
[RGB color space]: https://en.wikipedia.org/wiki/RGB_color_spaces
[hexadecimal representation]: https://en.wikipedia.org/wiki/RGB_color_model#Numeric_representations
While the `Rgb` format is most common, there are also framebuffers that use a different color format.
For example, the [`PixelFormat::Bgr`] stores the three colors in inverted order, i.e. blue first and red last.
There are also buffers that don't support colors at all and can represent only grayscale pixels.
The `bootloader_api` crate reports such buffers as [`PixelFormat::U8`].
[`PixelFormat::Bgr`]: https://docs.rs/bootloader_api/0.11.5/bootloader_api/info/enum.PixelFormat.html#variant.Bgr
[`PixelFormat::U8`]: https://docs.rs/bootloader_api/0.11.5/bootloader_api/info/enum.PixelFormat.html#variant.U8
Note that there might be some additional padding at the pixel-level as well.
For example, an `Rgb` pixel might be stored as 4 bytes instead of 3 to ensure 32-bit alignment.
The number of bytes per pixel is reported by the bootloader in the [`FrameBufferInfo::bytes_per_pixel`] field.
[`FrameBufferInfo::bytes_per_pixel`]: https://docs.rs/bootloader_api/0.11/bootloader_api/info/struct.FrameBufferInfo.html#structfield.bytes_per_pixel
## Setting specific Pixels
Based on this above details, we can now create a function to set a specific pixel to a certain color.
We start by creating a new `framebuffer` [module]:
[module]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
```rust ,hl_lines=3-5
// in kernel/src/main.rs
// declare a submodule -> the compiler will automatically look
// for a file named `framebuffer.rs` or `framebuffer/mod.rs`
mod framebuffer;
```
In the new module, we create basic structs for representing pixel positions and colors:
```rust ,hl_lines=3-16
// in new kernel/src/framebuffer.rs file
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Position {
pub x: usize,
pub y: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color {
pub red: u8,
pub green: u8,
pub blue: u8,
}
```
By marking the structs and their fields as `pub`, we make them accessible from the parent `kernel` module.
We use the `#[derive]` attribute to implement the [`Debug`], [`Clone`], [`Copy`], [`PartialEq`], and [`Eq`] traits of Rust's core library.
These traits allow us to duplicate, compare, and print the structs.
[`Debug`]: https://doc.rust-lang.org/stable/core/fmt/trait.Debug.html
[`Clone`]: https://doc.rust-lang.org/stable/core/clone/trait.Clone.html
[`Copy`]: https://doc.rust-lang.org/stable/core/marker/trait.Copy.html
[`PartialEq`]: https://doc.rust-lang.org/stable/core/cmp/trait.PartialEq.html
[`Eq`]: https://doc.rust-lang.org/stable/core/cmp/trait.Eq.html
Next, we create a function for setting a specific pixel in the framebuffer to a given color:
```rust ,hl_lines=3 5-39
// in new kernel/src/framebuffer.rs file
use bootloader_api::info::{FrameBuffer, PixelFormat};
pub fn set_pixel_in(framebuffer: &mut FrameBuffer, position: Position, color: Color) {
let info = framebuffer.info();
// calculate offset to first byte of pixel
let byte_offset = {
// use stride to calculate pixel offset of target line
let line_offset = position.y * info.stride;
// add x position to get the absolute pixel offset in buffer
let pixel_offset = line_offset + position.x;
// convert to byte offset
pixel_offset * info.bytes_per_pixel
};
// set pixel based on color format
let pixel_buffer = &mut framebuffer.buffer_mut()[byte_offset..];
match info.pixel_format {
PixelFormat::Rgb => {
pixel_buffer[0] = color.red;
pixel_buffer[1] = color.green;
pixel_buffer[2] = color.blue;
}
PixelFormat::Bgr => {
pixel_buffer[0] = color.blue;
pixel_buffer[1] = color.green;
pixel_buffer[2] = color.red;
}
PixelFormat::U8 => {
// use a simple average-based grayscale transform
let gray = color.red / 3 + color.green / 3 + color.blue / 3;
pixel_buffer[0] = gray;
}
other => panic!("unknown pixel format {other:?}"),
}
}
```
The first step is to calculate the byte offset within the framebuffer slice at which the pixel starts.
For this, we first calculate the pixel offset of the line by multiplying the `y` position with the stride of the framebuffer, i.e. its line width plus the line padding.
We then add the `x` position to get the absolute index of the pixel.
As the framebuffer slice is a byte slice, we need to transform the pixel index to a byte offset by multiplying it with the number of `bytes_per_pixel`.
[`FrameBuffer::buffer_mut`]: https://docs.rs/bootloader_api/0.11.5/bootloader_api/info/struct.FrameBuffer.html#method.buffer_mut
The second step is to set the pixel to the desired color.
We first use the [`FrameBuffer::buffer_mut`] method to get access to the actual bytes of the framebuffer in form of a slice.
Then, we use the slicing operator `[byte_offset..]` to get a sub-slice starting at the `byte_offset` of the target pixel.
As the write operation depends on the pixel format, we use a [`match`] statement:
[`match`]: https://doc.rust-lang.org/stable/std/keyword.match.html
- For `Rgb` framebuffers, we write three bytes; first red, then green, then blue.
- For `Bgr` framebuffers, we also write three bytes, but blue first and red last.
- For `U8` framebuffers, we first convert the color to grayscale by taking the average of the three color channels.
Note that there are multiple [different ways to convert colors to grayscale], so you can also use different factors here.
- For all other framebuffer formats, we [panic] for now.
[different ways to convert colors to grayscale]: https://www.baeldung.com/cs/convert-rgb-to-grayscale#bd-convert-rgb-to-grayscale
[panic]: https://doc.rust-lang.org/stable/core/macro.panic.html
Let's try to use our new function to write a blue pixel in our `kernel_main` function:
```rust ,hl_lines=5-11
// in kernel/src/main.rs
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
if let Some(framebuffer) = boot_info.framebuffer.as_mut() {
let position = framebuffer::Position { x: 20, y: 100 };
let color = framebuffer::Color {
red: 0,
green: 0,
blue: 255,
};
framebuffer::set_pixel_in(framebuffer, position, color);
}
loop {}
}
```
When we run our code in QEMU using `cargo run --bin qemu-bios` (or `--bin qemu-uefi`) and look _very closely_, we can see the blue pixel.
It's really difficult to see, so I marked with an arrow below:
![QEMU the bootloader text output with one pixel set to blue. An annotated arrow points to the pixel](qemu-blue-pixel.png)
As this single pixel is too difficult to see, let's draw a filled square of 100x100 pixels instead:
```rust ,hl_lines=10-18
// in kernel/src/main.rs
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
if let Some(framebuffer) = boot_info.framebuffer.as_mut() {
let color = framebuffer::Color {
red: 0,
green: 0,
blue: 255,
};
for x in 0..100 {
for y in 0..100 {
let position = framebuffer::Position {
x: 20 + x,
y: 100 + y,
};
framebuffer::set_pixel_in(framebuffer, position, color);
}
}
}
loop {}
}
```
Now we clearly see that our code works as intended:
![QEMU showing a blue square above the bootloader text output](qemu-blue-square.png)
Feel free to experiment with different positions and colors if you like.
You can also try to draw a circle instead of a square, or a line with a certain thickness.
As you can probably imagine, it would be a lot of work to draw more complex shapes this way.
One example for such complex shapes is _text_, i.e. the rendering of letters and punctuation.
Fortunately, there is the nice `no_std`-compatible [`embedded-graphics`] crate, which provides draw functions for text, various shapes, and image data.
[`embedded-graphics`]: https://docs.rs/embedded-graphics/latest/embedded_graphics/index.html
## The `embedded-graphics` crate
### Implementing `DrawTarget`
```rust ,hl_lines=3
// in kernel/src/framebuffer.rs
use embedded_graphics::{
Pixel,
draw_target::DrawTarget,
geometry::{OriginDimensions, Size},
pixelcolor::{Rgb888, RgbColor},
};
pub struct Display<'f> {
framebuffer: &'f mut FrameBuffer,
}
impl<'f> Display<'f> {
pub fn new(framebuffer: &'f mut FrameBuffer) -> Display {
Display { framebuffer }
}
fn draw_pixel(&mut self, Pixel(coordinates, color): Pixel<Rgb888>) {
// ignore any out of bounds pixels
let (width, height) = {
let info = self.framebuffer.info();
(info.width, info.height)
};
let (x, y) = {
let c: (i32, i32) = coordinates.into();
(c.0 as usize, c.1 as usize)
};
if (0..width).contains(&x) && (0..height).contains(&y) {
let color = Color { red: color.r(), green: color.g(), blue: color.b() };
set_pixel_in(self.framebuffer, Position { x, y }, color);
}
}
}
impl<'f> DrawTarget for Display<'f> {
type Color = Rgb888;
/// Drawing operations can never fail.
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for pixel in pixels.into_iter() {
self.draw_pixel(pixel);
}
Ok(())
}
}
impl<'f> OriginDimensions for Display<'f> {
fn size(&self) -> Size {
let info = self.framebuffer.info();
Size::new(info.width as u32, info.height as u32)
}
}
```
---
So far, we have drawn shapes and pixels directly onto the framebuffer. That's fine and all, but how is one able to go from that to displaying text on the screen? Understanding this requires taking a deep dive into how characters are rendered behind the scenes.
When a key is pressed on the keyboard, it sends a character code to the CPU. It's the CPU's job at that point to then interpret the character code and match it with an image to draw on the screen. The image is then sent to either the GPU or the framebuffer (the latter in our case) to be drawn on the screen, and the user sees that image as a letter, number, CJK character, emoji, or whatever else he or she wanted to have displayed by pressing that key.
In most other programming languages, implementing this behind the scenes can be a daunting task. With Rust, however, we have a toolset at our disposal that can pave the way for setting up proper framebuffer logging using very little code of our own.
# The `log` crate
Rust developers used to writing user-mode code will recognize the `log` crate from a mile away:
```toml
# in Cargo.toml
[dependencies]
log = { version = "0.4.17", default-features = false }
```
This crate has both a set of macros for logging either to the console or to a log file for later reading and a trait — also called `Log` with a capital L — that can be implemented to provide a backend, called a `Logger` in Rust parlance. Loggers are provided by a myriad of crates for a wide variety of use cases, and some of them even run on bare metal. We already used one such extant logger in the UEFI booting module when we used the logger provided by the `uefi` crate to print text to the UEFI console. That won't work in the kernel, however, because UEFI boot services need to be active in order for the UEFI logger to be usable.
If you were paying attention to the post before that one, however, you may have noticed that the bootloader is itself able to log directly to the framebuffer as it did when we booted the barebones kernel for the first time, and unlike the UEFI console logger, this logger is usable long after UEFI boot services are exited. It's this logger, therefore, that provides the easiest means of implementation on our end.
## `bootloader-x86_64-common`
In version 0.11.x of the bootloader crate, each component is separate, unlike in 0.10.x where the bootloader was a huge monolith. This is fantastic as it means that a lot of the APIs that the bootloader uses behind the scenes are also free for kernels to use, including, of course, the logger. The set of APIs that the logger belongs to are in a crate called `bootloader-x86_64-common` which also contains some other useful abstractions related to things like memory management that will come in handy later:
```toml
# in Cargo.toml
[dependencies]
bootloader-x86_64-common = "0.11.3"
```
For now, however, only the logger will be used. If you are curious as to how this logger is written behind the scenes, however, don't worry; a sub-module of this chapter will include a tutorial on how to write a custom logger from scratch.
# Putting it all together
Before we use the bootloader's logger, we first need to initialize it. This requires creating a static instance, since it needs to live for as long as the kernel lives — which would mean for as long as the computer is powered on. Unfortunately, this is easier said than done, as Rust statics can be rather finicky — understandably so for security reasons. Luckily, there's a crate for this too.
## The `conquer_once` crate
Those used to using the standard library know that it provides a `OnceCell` which is exactly what it sounds like: you write to it only once, and then after that it's just there to use whenever. We're in a kernel and don't have access to the standard library, however, so is there a crate on crates.io that provides a replacement? Ah, yes there is:
```toml
# in Cargo.toml
[dependencies]
conquer-once = { version = "0.4.0", default-features = false }
```
Note that we need to add `default-features = false` to our `conquer-once` dependency —that's because the [`conquer-once` crate](https://crates.io/crates/conquer-once) tries to pull in the standard library by default, which in the kernel will result in compilation errors.
Now that we've added our two dependencies, it's time to use them:
```rust
// in src/main.rs
use conquer_once::spin::OnceCell;
use bootloader_x86_64_common::logger::LockedLogger;
// ...
pub(crate) static LOGGER: OnceCell<LockedLogger> = OnceCell::uninit();
```
By setting the logger up as a static `OnceCell` it becomes much easier to initialize. We use `pub(crate)` to ensure that the kernel can see it but nothing else can.
After this, it's time to actually initialize it. To do that, we use a function:
```rust
// in src/main.rs
use bootloader_api::info::FrameBufferInfo;
// ...
pub(crate) fn init_logger(buffer: &'static mut [u8], info: FrameBufferInfo) {
let logger = LOGGER.get_or_init(move || LockedLogger::new(buffer, info, true, false));
log::set_logger(logger).expect("Logger already set");
log::set_max_level(log::LevelFilter::Trace);
log::info!("Hello, Kernel Mode!");
}
```
This function takes two parameters: a byte slice representing a raw framebuffer and a `FrameBufferInfo` structure containing information about the first parameter. Getting those parameters, however, requires jumping through some hoops to satisfy the borrow checker:
```rust
// in src/main.rs
fn kernel_main(boot_info: &'static mut bootloader_api::BootInfo) -> ! {
// ...
// free the doubly wrapped framebuffer from the boot info struct
let frame_buffer_optional = &mut boot_info.framebuffer;
// free the wrapped framebuffer from the FFI-safe abstraction provided by bootloader_api
let frame_buffer_option = frame_buffer_optional.as_mut();
// unwrap the framebuffer
let frame_buffer_struct = frame_buffer_option.unwrap();
// extract the framebuffer info and, to satisfy the borrow checker, clone it
let frame_buffer_info = frame_buffer_struct.info().clone();
// get the framebuffer's mutable raw byte slice
let raw_frame_buffer = frame_buffer_struct.buffer_mut();
// finally, initialize the logger using the last two variables
init_logger(raw_frame_buffer, frame_buffer_info);
// ...
}
```
Any one of these steps, if skipped, will cause the borrow checker to throw a hissy fit due to the use of the `move ||` closure by the initializer function. With this, however, you're done, and you'll know the logger has been initialized when you see "Hello, Kernel Mode!" printed on the screen.
<!-- more -->
<!-- toc -->
<!-- TODO: update relative link in 02-booting/uefi/index.md when this post is finished --> <!-- TODO: update relative link in 02-booting/uefi/index.md when this post is finished -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -22,13 +22,13 @@
*/ */
/* Fonts */ /* Fonts */
@font-face { @font-face {
font-family: "Iosevka"; font-family: 'Iosevka Web';
src: url("/fonts/iosevka-regular.woff2") format("woff2"), url("/fonts/iosevka-regular.woff") format("woff"); font-display: swap;
font-weight: normal; font-weight: 400;
font-style: normal; font-stretch: normal;
font-display: swap; font-style: normal;
src: url('/fonts/ttf/iosevka-regular.ttf') format('truetype'), url('/fonts/woff2/iosevka-regular.woff2') format('woff2');
} }
/* /*
@@ -221,7 +221,7 @@ abbr[title] {
/* Code */ /* Code */
code, code,
pre { pre {
font-family: "Iosevka", monospace; font-family: "Iosevka Web", monospace;
} }
code { code {
padding: 0.25em 0.5em; padding: 0.25em 0.5em;

View File

@@ -22,13 +22,29 @@
*/ */
/* Fonts */ /* Fonts */
@font-face { @font-face {
font-family: "Iosevka"; font-family: 'Iosevka Web';
src: url("/fonts/iosevka-regular.woff2") format("woff2"), url("/fonts/iosevka-regular.woff") format("woff"); font-display: swap;
font-weight: normal; font-weight: 400;
font-style: normal; font-stretch: normal;
font-display: swap; font-style: normal;
src: url('/fonts/ttf/iosevka-regular.ttf') format('truetype'), url('/fonts/woff2/iosevka-regular.woff2') format('woff2');
}
@font-face {
font-family: 'Iosevka Web';
font-display: swap;
font-weight: 700;
font-stretch: normal;
font-style: normal;
src: url('ttf/iosevka-bold.ttf') format('truetype'), url('woff2/iosevka-bold.woff2') format('woff2');
}
@font-face {
font-family: 'Iosevka Web';
font-display: swap;
font-weight: 400;
font-stretch: normal;
font-style: italic;
src: url('ttf/iosevka-italic.ttf') format('truetype'), url('woff2/iosevka-italic.woff2') format('woff2');
} }
/* /*
@@ -60,7 +76,7 @@ html {
--background-color: #fff; --background-color: #fff;
--text-color: #515151; --text-color: #515151;
--heading-color: #313131; --heading-color: #313131;
--heading-code-color: #a0565c; --heading-code-color: #313131;
--link-color: #268bd2; --link-color: #268bd2;
--hr-color-top: #eee; --hr-color-top: #eee;
--hr-color-bottom: #fff; --hr-color-bottom: #fff;
@@ -77,8 +93,8 @@ html {
--border-color-section-basic-io: #99ff99; --border-color-section-basic-io: #99ff99;
// colors with transparency that work for both light and dark mode // colors with transparency that work for both light and dark mode
--background-color-note: #00ff001a; --background-color-note: #6666cc1a;
--border-color-note: #00ff0050; --border-color-note: #6666cc50;
} }
@mixin set-colors-dark { @mixin set-colors-dark {
@@ -165,11 +181,11 @@ h6 {
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
h1 { h1 {
font-size: 2rem; font-size: 2.25rem;
} }
h2 { h2 {
margin-top: 1rem; margin-top: 1rem;
font-size: 1.5rem; font-size: 1.75rem;
} }
h3 { h3 {
margin-top: 1.5rem; margin-top: 1.5rem;
@@ -180,6 +196,7 @@ h5,
h6 { h6 {
margin-top: 1rem; margin-top: 1rem;
font-size: 1rem; font-size: 1rem;
font-style: italic;
} }
/* Body text */ /* Body text */
@@ -243,7 +260,7 @@ abbr[title] {
/* Code */ /* Code */
code, code,
pre { pre {
font-family: "Iosevka", monospace; font-family: "Iosevka Web", monospace;
} }
code { code {
padding: 0.25em 0.5em; padding: 0.25em 0.5em;
@@ -263,6 +280,19 @@ pre {
overflow: auto; overflow: auto;
word-wrap: normal; word-wrap: normal;
background-color: var(--code-background-color); background-color: var(--code-background-color);
&.language-rust, &.language-toml {
padding-left: 15px;
mark {
border-left: 3px solid rgba(0, 200, 0, 1);
margin-left: -10px;
padding-left: 7px;
font-weight: bold;
}
}
&.language-bash mark {
font-weight: bold;
color: rgba(255, 255, 255, 1);
}
} }
pre code { pre code {
padding: 0; padding: 0;
@@ -270,6 +300,15 @@ pre code {
color: inherit; color: inherit;
background-color: transparent; background-color: transparent;
} }
pre mark {
// make highlights take the full width
display: block;
// use the code color (instead of black)
color: inherit;
// override the hardcoded background color (set to black by the theme)
background-color: transparent !important;
}
.highlight { .highlight {
margin-bottom: 1rem; margin-bottom: 1rem;
border-radius: 4px; border-radius: 4px;
@@ -1156,3 +1195,18 @@ img {
opacity: 1; opacity: 1;
} }
} }
.post-extra-content {
margin-top: 1rem;
h4 {
display: inline;
margin-right: 1rem;
}
a {
font-weight: bold;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -51,7 +51,7 @@
<small> <small>
&copy; <time datetime="2022">2022</time>. All rights reserved. &copy; <time datetime="2022">2022</time>. All rights reserved.
<a class="spaced" href="https://github.com/phil-opp/blog_os#license">License</a> <a class="spaced" href="https://github.com/phil-opp/blog_os#license">License</a>
<a class="spaced" href="{{ get_url(path=" @/pages/contact.md") | safe }}">Contact</a> <a class="spaced" href="{{ get_url(path="@/pages/contact.md") | safe }}">Contact</a>
</small> </small>
</footer> </footer>
</div> </div>

View File

@@ -65,7 +65,7 @@
<p>{{ status_updates.description }}</p> <p>{{ status_updates.description }}</p>
<ul> <ul>
{% include "auto/status-updates-truncated.html" %} {% include "auto/status-updates-truncated.html" %}
<li><a href="{{ get_url(path=" @/status-update/_index.md") | safe }}"><em>view all »</em></a></li> <li><a href="{{ get_url(path="@/status-update/_index.md") | safe }}"><em>view all »</em></a></li>
</ul> </ul>
</div> </div>
@@ -75,7 +75,7 @@
editions, you can still find them here:</p> editions, you can still find them here:</p>
<ul> <ul>
<li> <li>
<p><strong><a href="{{ get_url(path=" @/edition-2/_index.md")}}">Second Edition:</a></strong> The second <p><strong><a href="{{ get_url(path="@/edition-2/_index.md")}}">Second Edition:</a></strong> The second
edition is based on older version of the <code>bootloader</code> crate, which uses the hardware-provided edition is based on older version of the <code>bootloader</code> crate, which uses the hardware-provided
VGA text buffer instead of a pixel-based framebuffer for screen output. Instead of the APIC, the legacy VGA text buffer instead of a pixel-based framebuffer for screen output. Instead of the APIC, the legacy
PIC is used for implementing hardware interrupts. The second edition only works on BIOS-based systems, PIC is used for implementing hardware interrupts. The second edition only works on BIOS-based systems,
@@ -83,7 +83,7 @@
}}"><em>read&nbsp;the&nbsp;second edition&nbsp;»</em></a></p> }}"><em>read&nbsp;the&nbsp;second edition&nbsp;»</em></a></p>
</li> </li>
<li> <li>
<p><strong><a href="{{ get_url(path=" @/edition-1/_index.md")}}">First Edition:</a></strong> The first <p><strong><a href="{{ get_url(path="@/edition-1/_index.md")}}">First Edition:</a></strong> The first
edition was already started in 2015. It is very different in many aspects, for example it builds upon edition was already started in 2015. It is very different in many aspects, for example it builds upon
the GRUB bootloader instead of using the `bootloader` crate. This means that it requires you to manually the GRUB bootloader instead of using the `bootloader` crate. This means that it requires you to manually
write some assembly code and do an elaborate remap of the kernel's virtual pages in order to improve write some assembly code and do an elaborate remap of the kernel's virtual pages in order to improve

View File

@@ -82,7 +82,7 @@
still in alpha state, so things might be still in progress, not work, or change without warning! still in alpha state, so things might be still in progress, not work, or change without warning!
</p> </p>
<p> <p>
For a more stable experience, check out the current <a href="{{ get_url(path = " @/edition-2/_index.md") | safe For a more stable experience, check out the current <a href="{{ get_url(path = "@/edition-2/_index.md") | safe
}}"><strong>Second Edition</strong></a>. }}"><strong>Second Edition</strong></a>.
</p> </p>
</div> </div>

View File

@@ -6,10 +6,10 @@
{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %} {% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
{% block header %} {% block header %}
{% if lang != "en" -%} {% if lang != "en" -%}
<aside id="all-posts-link"><a href="{{ get_url(path=" @/edition-3/_index.md") }}/{{ lang }}" title="All Posts">{{ <aside id="all-posts-link"><a href="{{ get_url(path="@/edition-3/_index.md") }}/{{ lang }}" title="All Posts">{{
trans(key="all_posts", lang=lang) }}</a></aside> trans(key="all_posts", lang=lang) }}</a></aside>
{%- else -%} {%- else -%}
<aside id="all-posts-link"><a href="{{ get_url(path=" @/edition-3/_index.md") }}" title="All Posts">{{ <aside id="all-posts-link"><a href="{{ get_url(path="@/edition-3/_index.md") }}" title="All Posts">{{
trans(key="all_posts", lang=lang) }}</a></aside> trans(key="all_posts", lang=lang) }}</a></aside>
{%- endif %} {%- endif %}
{% endblock header %} {% endblock header %}

View File

@@ -10,9 +10,11 @@
{% macro giscus(search_term, lang) %} {% macro giscus(search_term, lang) %}
{% if lang != "en" %} {% if lang != "en" %}
{% set category = "Post Comments (translated)" %} {% set category = "Post Comments (translated)" %}
{% set category_id = "DIC_kwDOAlvePc4CPg4c" %}
{% set category_path = "post-comments-translated" %} {% set category_path = "post-comments-translated" %}
{% else %} {% else %}
{% set category = "Post Comments" %} {% set category = "Post Comments" %}
{% set category_id = "MDE4OkRpc2N1c3Npb25DYXRlZ29yeTMzMDE4OTg1" %}
{% set category_path = "post-comments" %} {% set category_path = "post-comments" %}
{% endif %} {% endif %}
@@ -33,7 +35,7 @@
data-repo="phil-opp/blog_os" data-repo="phil-opp/blog_os"
data-repo-id="MDEwOlJlcG9zaXRvcnkzOTU3NTEwMQ==" data-repo-id="MDEwOlJlcG9zaXRvcnkzOTU3NTEwMQ=="
data-category="{{ category }}" data-category="{{ category }}"
data-category-id="MDE4OkRpc2N1c3Npb25DYXRlZ29yeTMzMDE4OTg1" data-category-id="{{ category_id }}"
{% if search_term is number %} {% if search_term is number %}
data-mapping="number" data-mapping="number"
{% else %} {% else %}