diff --git a/README.md b/README.md
index 5afce02a..da654e64 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,8 @@ The goal of this project is to provide step-by-step tutorials in individual blog
The current version of the blog is already the second edition. The first edition is outdated and no longer maintained, but might still be useful. The posts of the first edition are:
+Click to expand
+
**Bare Bones:**
- [A Minimal x86 Kernel](https://os.phil-opp.com/multiboot-kernel.html)
@@ -110,6 +112,8 @@ The current version of the blog is already the second edition. The first edition
- [Returning from Exceptions](https://os.phil-opp.com/returning-from-exceptions.html)
([source code](https://github.com/phil-opp/blog_os/tree/returning_from_exceptions))
+
+
## License
This project, with exception of the `blog/content` folder, is licensed under either of
diff --git a/blog/content/_index.fa.md b/blog/content/_index.fa.md
new file mode 100644
index 00000000..493e0c96
--- /dev/null
+++ b/blog/content/_index.fa.md
@@ -0,0 +1,13 @@
++++
+template = "edition-2/index.html"
++++
+
+
Writing an OS in Rust
+
+
+
+This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
+
+Latest post:
+
+
+
+This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
+
+Latest post:
+
+
+
+This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
+
+Latest post:
+
+
+
+This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
+
+Latest post:
+
+
+
+This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
+
+Latest post:
+
+
+
+[lazy_static]: https://docs.rs/lazy_static/1.0.1/lazy_static/
+
+私達のプロジェクトに`lazy_static`クレートを追加しましょう:
+
+```toml
+# in Cargo.toml
+
+[dependencies.lazy_static]
+version = "1.0"
+features = ["spin_no_std"]
+```
+
+標準ライブラリをリンクしないので、`spin_no_std`機能が必要です。
+
+`lazy_static`を使えば、静的な`WRITER`が問題なく定義できます:
+
+```rust
+// in src/vga_buffer.rs
+
+use lazy_static::lazy_static;
+
+lazy_static! {
+ pub static ref WRITER: Writer = Writer {
+ column_position: 0,
+ color_code: ColorCode::new(Color::Yellow, Color::Black),
+ buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ };
+}
+```
+
+しかし、この`WRITER`は不変なので、全く使い物になりません。なぜならこれは、この`WRITER`に何も書き込めないということを意味するからです(私達のすべての書き込みメソッドは`&mut self`を取るからです)。ひとつの解決策には、[可変で静的な変数][mutable static]を使うということがあります。しかし、そうすると、あらゆる読み書きが容易にデータ競合やその他の良くないことを引き起こしてしまうので、それらがすべてunsafeになってしまいます。`static mut`を使うことも、[それを削除しようという提案][remove static mut]すらあることを考えると、できる限り避けたいです。しかし他に方法はあるのでしょうか?不変静的変数を[RefCell]や、果ては[UnsafeCell]のような、[内部可変性][interior mutability]を提供するcell型と一緒に使うという事も考えられます。しかし、それらの型は(ちゃんとした理由があって)[Sync]ではないので、静的変数で使うことはできません。
+
+[mutable static]: https://doc.rust-jp.rs/book-ja/ch19-01-unsafe-rust.html#可変で静的な変数にアクセスしたり変更する
+[remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437
+[RefCell]: https://doc.rust-jp.rs/book-ja/ch15-05-interior-mutability.html#refcelltで実行時に借用を追いかける
+[UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html
+[interior mutability]: https://doc.rust-jp.rs/book-ja/ch15-05-interior-mutability.html
+[Sync]: https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html
+
+### スピンロック
+同期された内部可変性を得るためには、標準ライブラリを使えるなら[Mutex]を使うことができます。これは、リソースがすでにロックされていた場合、スレッドをブロックすることにより相互排他性を提供します。しかし、私達の初歩的なカーネルにはブロックの機能はもちろんのこと、スレッドの概念すらないので、これも使うことはできません。しかし、コンピュータサイエンスの世界には、OSを必要としない非常に単純なmutexが存在するのです:それが[スピンロック][spinlock]です。スピンロックを使うと、ブロックする代わりに、スレッドは単純にリソースを何度も何度もロックしようとすることで、mutexが開放されるまでの間CPU時間を使い尽くします。
+
+[Mutex]: https://doc.rust-lang.org/nightly/std/sync/struct.Mutex.html
+[spinlock]: https://ja.wikipedia.org/wiki/スピンロック
+
+スピンロックによるmutexを使うには、[spinクレート][spin crate]への依存を追加すればよいです:
+
+[spin crate]: https://crates.io/crates/spin
+
+```toml
+# in Cargo.toml
+[dependencies]
+spin = "0.5.2"
+```
+
+すると、スピンを使ったMutexを使うことができ、静的な`WRITER`に安全な[内部可変性][interior mutability]を追加できます。
+
+```rust
+// in src/vga_buffer.rs
+
+use spin::Mutex;
+...
+lazy_static! {
+ pub static ref WRITER: Mutex = Mutex::new(Writer {
+ column_position: 0,
+ color_code: ColorCode::new(Color::Yellow, Color::Black),
+ buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
+ });
+}
+```
+`print_something`関数を消して、`_start`関数から直接出力しましょう:
+
+```rust
+// in src/main.rs
+#[no_mangle]
+pub extern "C" fn _start() -> ! {
+ use core::fmt::Write;
+ vga_buffer::WRITER.lock().write_str("Hello again").unwrap();
+ write!(vga_buffer::WRITER.lock(), ", some numbers: {} {}", 42, 1.337).unwrap();
+
+ loop {}
+}
+```
+`fmt::Write`トレイトの関数を使うためには、このトレイトをインポートする必要があります。
+
+### 安全性
+コードにはunsafeブロックが一つ(`0xb8000`を指す参照`Buffer`を作るために必要なもの)しかないことに注目してください。その後は、すべての命令が安全です。Rustは配列アクセスにはデフォルトで境界チェックを行うので、間違ってバッファの外に書き込んでしまうことはありえません。よって、必要とされる条件を型システムにすべて組み込んだので、安全なインターフェースを外部に提供できます。
+
+### printlnマクロ
+大域的なwriterを手に入れたので、プログラムのどこでも使える`println`マクロを追加できます。Rustの[マクロの構文][macro syntax]はすこしややこしいので、一からマクロを書くことはしません。代わりに、標準ライブラリで[`println!`マクロ][`println!` macro]のソースを見てみます:
+
+[macro syntax]: https://doc.rust-lang.org/nightly/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming
+[`println!` macro]: https://doc.rust-lang.org/nightly/std/macro.println!.html
+
+```rust
+#[macro_export]
+macro_rules! println {
+ () => (print!("\n"));
+ ($($arg:tt)*) => (print!("{}\n", format_args!($($arg)*)));
+}
+```
+
+マクロは1つ以上のルールを使って定義されます(`match`アームと似ていますね)。`println`には2つのルールがあります:1つ目は引数なし呼び出し(例えば `println!()`)のためのもので、これは`print!("\n")`に展開され、よってただ改行を出力するだけになります。2つ目のルールはパラメータ付きの呼び出し(例えば`println!("Hello")`や `println!("Number: {}", 4)`)のためのものです。これも`print!`マクロの呼び出しへと展開され、すべての引数に加え、改行`\n`を最後に追加して渡します。
+
+`#[macro_export]`属性はマクロを(その定義されたモジュールだけではなく)クレート全体および外部クレートで使えるようにします。また、これはマクロをクレートルートに置くため、`std::macros::println`の代わりに`use std::println`を使ってマクロをインポートしないといけないということを意味します。
+
+[`print!`マクロ][`print!` macro]は以下のように定義されています:
+
+[`print!` macro]: https://doc.rust-lang.org/nightly/std/macro.print!.html
+
+```rust
+#[macro_export]
+macro_rules! print {
+ ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
+}
+```
+
+このマクロは`io`モジュール内の[`_print`関数][`_print` function]の呼び出しへと展開しています。[`$crate`という変数][`$crate` variable]は、他のクレートで使われた際、`std`へと展開することによって、マクロが`std`クレートの外側で使われたとしてもうまく動くようにしてくれます。
+
+[`format_args`マクロ][`format_args` macro]が与えられた引数から[fmt::Arguments]型を作り、これが`_print`へと渡されています。libstdの[`_print`関数]は`print_to`を呼び出すのですが、これは様々な`Stdout`デバイスをサポートいているためかなり煩雑です。ここではただVGAバッファに出力したいだけなので、そのような煩雑な実装は必要ありません。
+
+[`_print` function]: https://github.com/rust-lang/rust/blob/29f5c699b11a6a148f097f82eaa05202f8799bbc/src/libstd/io/stdio.rs#L698
+[`$crate` variable]: https://doc.rust-lang.org/1.30.0/book/first-edition/macros.html#the-variable-crate
+[`format_args` macro]: https://doc.rust-lang.org/nightly/std/macro.format_args.html
+[fmt::Arguments]: https://doc.rust-lang.org/nightly/core/fmt/struct.Arguments.html
+
+VGAバッファに出力するには、`println!`マクロと`print!`マクロをコピーし、独自の`_print`関数を使うように修正してやればいいです:
+
+```rust
+// in src/vga_buffer.rs
+
+#[macro_export]
+macro_rules! print {
+ ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
+}
+
+#[macro_export]
+macro_rules! println {
+ () => ($crate::print!("\n"));
+ ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
+}
+
+#[doc(hidden)]
+pub fn _print(args: fmt::Arguments) {
+ use core::fmt::Write;
+ WRITER.lock().write_fmt(args).unwrap();
+}
+```
+
+元の`println`の定義と異なり、`print!`マクロの呼び出しにも`$crate`をつけるようにしています。これにより、`println`だけを使いたいと思ったら`print!`マクロもインポートしなくていいようになります。
+
+標準ライブラリのように、`#[macro_export]`属性を両方のマクロに与え、クレートのどこでも使えるようにします。このようにすると、マクロはクレートの名前空間のルートに置かれるので、`use crate::vga_buffer::println`としてインポートするとうまく行かないことに注意してください。代わりに、 `use crate::println`としなければいけません。
+
+`_print`関数は静的な`WRITER`をロックし、その`write_fmt`メソッドを呼び出します。このメソッドは`Write`トレイトのものなので、このトレイトもインポートしないといけません。最後に追加した`unwrap()`は、画面出力がうまく行かなかったときパニックします。しかし、`write_str`は常に`Ok`を返すようにしているので、これは起きないはずです。
+
+マクロは`_print`をモジュールの外側から呼び出せる必要があるので、この関数は公開されていなければなりません。しかし、これは非公開の実装の詳細であると考え、[`doc(hidden)`属性][`doc(hidden)` attribute]をつけることで、生成されたドキュメントから隠すようにします。
+
+[`doc(hidden)` attribute]: https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#dochidden
+
+### `println`を使ってHello World
+こうすることで、`_start`関数で`println`を使えるようになります:
+
+```rust
+// in src/main.rs
+
+#[no_mangle]
+pub extern "C" fn _start() {
+ println!("Hello World{}", "!");
+
+ loop {}
+}
+```
+
+マクロはすでに名前空間のルートにいるので、main関数内でマクロをインポートしなくても良いということに注意してください。
+
+期待通り、画面に Hello World! と出ています:
+
+
+
+### パニックメッセージを出力する
+
+`println`マクロを手に入れたので、これを私達のパニック関数で使って、パニックメッセージとパニックの場所を出力させることができます:
+
+```rust
+// in main.rs
+
+/// この関数はパニック時に呼ばれる。
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ println!("{}", info);
+ loop {}
+}
+```
+`panic!("Some panic message");`という文を`_start`関数に書くと、次の出力を得ます:
+
+
+
+つまり、パニックが起こったということだけでなく、パニックメッセージとそれがコードのどこで起こったかまで知ることができます。
+
+## まとめ
+この記事では、VGAテキストバッファの構造と、どのようにすれば`0xb8000`番地におけるメモリマッピングを通じてそれに書き込みを行えるかを学びました。このメモリマップされたバッファへの書き込みというunsafeな操作をカプセル化し、安全で便利なインターフェースを外部に提供するRustモジュールを作りました。
+
+また、cargoのおかげでサードパーティのライブラリへの依存関係を簡単に追加できることも分かりました。`lazy_static`と`spin`という2つの依存先は、OS開発においてとても便利であり、今後の記事においても使っていきます。
+
+## 次は?
+次の記事ではRustに組み込まれている単体テストフレームワークをセットアップする方法を説明します。その後、この記事のVGAバッファモジュールに対する基本的な単体テストを作ります。
diff --git a/blog/content/edition-2/posts/04-testing/index.md b/blog/content/edition-2/posts/04-testing/index.md
index e3112419..15cb1e5b 100644
--- a/blog/content/edition-2/posts/04-testing/index.md
+++ b/blog/content/edition-2/posts/04-testing/index.md
@@ -214,6 +214,8 @@ For specifying the exit status, we create a `QemuExitCode` enum. The idea is to
We can now update our `test_runner` to exit QEMU after all tests ran:
```rust
+// in src/main.rs
+
fn test_runner(tests: &[&dyn Fn()]) {
println!("Running {} tests", tests.len());
for test in tests {
@@ -246,6 +248,8 @@ The problem is that `cargo test` considers all error codes other than `0` as fai
To work around this, `bootimage` provides a `test-success-exit-code` configuration key that maps a specified exit code to the exit code `0`:
```toml
+# in Cargo.toml
+
[package.metadata.bootimage]
test-args = […]
test-success-exit-code = 33 # (0x10 << 1) | 1
@@ -315,6 +319,8 @@ Like the `isa-debug-exit` device, the UART is programmed using port I/O. Since t
To make the serial port easily usable, we add `serial_print!` and `serial_println!` macros:
```rust
+// in src/serial.rs
+
#[doc(hidden)]
pub fn _print(args: ::core::fmt::Arguments) {
use core::fmt::Write;
@@ -406,6 +412,8 @@ To exit QEMU with an error message on a panic, we can use [conditional compilati
[conditional compilation]: https://doc.rust-lang.org/1.30.0/book/first-edition/conditional-compilation.html
```rust
+// in src/main.rs
+
// our existing panic handler
#[cfg(not(test))] // new attribute
#[panic_handler]
@@ -786,7 +794,7 @@ We make the modules public to make them usable from outside of our library. This
Now we can update our `main.rs` to use the library:
```rust
-// src/main.rs
+// in src/main.rs
#![no_std]
#![no_main]
diff --git a/blog/content/edition-2/posts/06-double-faults/index.md b/blog/content/edition-2/posts/06-double-faults/index.md
index c9a909ff..45a90bc8 100644
--- a/blog/content/edition-2/posts/06-double-faults/index.md
+++ b/blog/content/edition-2/posts/06-double-faults/index.md
@@ -264,7 +264,7 @@ lazy_static! {
We use `lazy_static` because Rust's const evaluator is not yet powerful enough to do this initialization at compile time. We define that the 0th IST entry is the double fault stack (any other IST index would work too). Then we write the top address of a double fault stack to the 0th entry. We write the top address because stacks on x86 grow downwards, i.e. from high addresses to low addresses.
-We don't have implemented memory management yet, so we don't have a proper way to allocate a new stack. Instead, we use a `static mut` array as stack storage for now. The `unsafe` is required because the compiler can't guarantee race freedom when mutable statics are accessed. It is important that it is a `static mut` and not an immutable `static`, because otherwise the bootloader will map it to a read-only page. We will replace this with a proper stack allocation in a later post, then the `unsafe` will be no longer needed at this place.
+We haven't implemented memory management yet, so we don't have a proper way to allocate a new stack. Instead, we use a `static mut` array as stack storage for now. The `unsafe` is required because the compiler can't guarantee race freedom when mutable statics are accessed. It is important that it is a `static mut` and not an immutable `static`, because otherwise the bootloader will map it to a read-only page. We will replace this with a proper stack allocation in a later post, then the `unsafe` will be no longer needed at this place.
Note that this double fault stack has no guard page that protects against stack overflow. This means that we should not do anything stack intensive in our double fault handler because a stack overflow might corrupt the memory below the stack.
@@ -301,7 +301,7 @@ lazy_static! {
}
```
-We use `lazy_static` again, because Rust's const evaluator is not powerful enough yet. We create a new GDT with a code segment and a TSS segment.
+As before, we use `lazy_static` again. We create a new GDT with a code segment and a TSS segment.
#### Loading the GDT
diff --git a/blog/content/edition-2/posts/09-paging-implementation/index.md b/blog/content/edition-2/posts/09-paging-implementation/index.md
index 9002398f..6ff0e3cb 100644
--- a/blog/content/edition-2/posts/09-paging-implementation/index.md
+++ b/blog/content/edition-2/posts/09-paging-implementation/index.md
@@ -920,7 +920,7 @@ This function uses iterator combinator methods to transform the initial `MemoryM
- First, we call the `iter` method to convert the memory map to an iterator of [`MemoryRegion`]s.
- Then we use the [`filter`] method to skip any reserved or otherwise unavailable regions. The bootloader updates the memory map for all the mappings it creates, so frames that are used by our kernel (code, data or stack) or to store the boot information are already marked as `InUse` or similar. Thus we can be sure that `Usable` frames are not used somewhere else.
- Afterwards, we use the [`map`] combinator and Rust's [range syntax] to transform our iterator of memory regions to an iterator of address ranges.
-- The next step is the most complicated: We convert each range to an iterator through the `into_iter` method and then choose every 4096th address using [`step_by`]. Since 4096 bytes (= 4 KiB) is the page size, we get the start address of each frame. The bootloader page aligns all usable memory areas so that we don't need any alignment or rounding code here. By using [`flat_map`] instead of `map`, we get an `Iterator` instead of an `Iterator>`.
+- Next, we use [`flat_map`] to transform the address ranges into an iterator of frame start addresses, choosing every 4096th address using [`step_by`]. Since 4096 bytes (= 4 KiB) is the page size, we get the start address of each frame. The bootloader page aligns all usable memory areas so that we don't need any alignment or rounding code here. By using [`flat_map`] instead of `map`, we get an `Iterator` instead of an `Iterator>`.
- Finally, we convert the start addresses to `PhysFrame` types to construct the an `Iterator`.
[`MemoryRegion`]: https://docs.rs/bootloader/0.6.4/bootloader/bootinfo/struct.MemoryRegion.html
@@ -951,7 +951,7 @@ unsafe impl FrameAllocator for BootInfoFrameAllocator {
}
```
-We first use an `usable_frames` method to get an iterator of usable frames from the memory map. Then, we use the [`Iterator::nth`] function to get the frame with index `self.next` (thereby skipping `(self.next - 1)` frames). Before returning that frame, we increase `self.next` by one so that we return the following frame on the next call.
+We first use the `usable_frames` method to get an iterator of usable frames from the memory map. Then, we use the [`Iterator::nth`] function to get the frame with index `self.next` (thereby skipping `(self.next - 1)` frames). Before returning that frame, we increase `self.next` by one so that we return the following frame on the next call.
[`Iterator::nth`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.nth
diff --git a/blog/content/edition-2/posts/10-heap-allocation/index.md b/blog/content/edition-2/posts/10-heap-allocation/index.md
index fff4e887..d63f7eba 100644
--- a/blog/content/edition-2/posts/10-heap-allocation/index.md
+++ b/blog/content/edition-2/posts/10-heap-allocation/index.md
@@ -715,8 +715,6 @@ Now we're ready to add a few test cases. First, we add a test that performs some
```rust
// in tests/heap_allocation.rs
-
-use blog_os::{serial_print, serial_println};
use alloc::boxed::Box;
#[test_case]
diff --git a/blog/static/css/edition-1/main.css b/blog/static/css/edition-1/main.css
index b5def467..1ac0f68e 100644
--- a/blog/static/css/edition-1/main.css
+++ b/blog/static/css/edition-1/main.css
@@ -1,11 +1,3 @@
-@font-face {
- font-family: "Iosevka";
- src: url("/fonts/iosevka-regular.woff2") format("woff2"), url("/fonts/iosevka-regular.woff") format("woff");
- font-weight: normal;
- font-style: normal;
- font-display: swap;
-}
-
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
padding: 0;
color: #a0565c;
diff --git a/blog/static/css/edition-1/poole.css b/blog/static/css/edition-1/poole.css
index 975e026e..d2d6b544 100644
--- a/blog/static/css/edition-1/poole.css
+++ b/blog/static/css/edition-1/poole.css
@@ -150,7 +150,7 @@ abbr[title] {
/* Code */
code,
pre {
- font-family: "Iosevka", monospace;
+ font-family: Menlo, Monaco, Consolas, monospace;
}
code {
padding: .25em .5em;
diff --git a/blog/static/css/edition-2/main.css b/blog/static/css/edition-2/main.css
index 40e7dcab..4a5c693e 100644
--- a/blog/static/css/edition-2/main.css
+++ b/blog/static/css/edition-2/main.css
@@ -454,6 +454,6 @@ a strong {
direction: rtl;
}
-.left-to-right, .right-to-left pre {
+.left-to-right, .right-to-left pre, .right-to-left table {
direction: ltr;
}
diff --git a/blog/templates/edition-1/index.html b/blog/templates/edition-1/index.html
index 31d4aae7..0d8a68ab 100644
--- a/blog/templates/edition-1/index.html
+++ b/blog/templates/edition-1/index.html
@@ -1,6 +1,6 @@
{% extends "edition-1/base.html" %}
-{% import "macros.html" as macros %}
+{% import "edition-1/macros.html" as macros %}
{% block title %}{{ config.title }}{% endblock title %}
diff --git a/blog/templates/edition-1/macros.html b/blog/templates/edition-1/macros.html
new file mode 100644
index 00000000..a6197dad
--- /dev/null
+++ b/blog/templates/edition-1/macros.html
@@ -0,0 +1,39 @@
+{% macro post_link(page) %}
+ {% set translations = page.translations | filter(attribute="lang", value=lang) -%}
+ {%- if translations -%}
+ {%- set post = get_page(path = translations.0.path) -%}
+ {%- else -%}
+ {%- set post = page -%}
+ {%- set not_translated = true -%}
+ {%- endif -%}
+
+
+{% endmacro toc %}
diff --git a/blog/templates/edition-1/section.html b/blog/templates/edition-1/section.html
index 3bbe0a8a..45d12fa3 100644
--- a/blog/templates/edition-1/section.html
+++ b/blog/templates/edition-1/section.html
@@ -1,6 +1,6 @@
{% extends "edition-1/base.html" %}
-{% import "macros.html" as macros %}
+{% import "edition-1/macros.html" as macros %}
{% block title %}{{ section.title }} | {{ config.title }}{% endblock title %}
diff --git a/blog/templates/edition-2/extra.html b/blog/templates/edition-2/extra.html
index bdd5a16a..93c98f59 100644
--- a/blog/templates/edition-2/extra.html
+++ b/blog/templates/edition-2/extra.html
@@ -1,6 +1,6 @@
{% extends "edition-2/base.html" %}
-{% import "macros.html" as macros %}
+{% import "snippets.html" as snippets %}
{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
@@ -17,6 +17,6 @@
Comments
- {{ macros::utterances() }}
+ {{ snippets::utterances() }}
{% endblock after_main %}
diff --git a/blog/templates/edition-2/index.html b/blog/templates/edition-2/index.html
index 80173a74..6084311a 100644
--- a/blog/templates/edition-2/index.html
+++ b/blog/templates/edition-2/index.html
@@ -1,6 +1,7 @@
{% extends "edition-2/base.html" %}
-{% import "macros.html" as macros %}
+{% import "edition-2/macros.html" as macros %}
+{% import "snippets.html" as snippets %}
{% block title %}{{ config.title }}{% endblock title %}
@@ -8,20 +9,10 @@
{% set posts_section = get_section(path = "edition-2/posts/_index.md") %}
{% set posts = posts_section.pages %}
-
Writing an OS in Rust
-
-
-
- This blog series creates a small operating system in the
- Rust programming language. Each post is a small tutorial and includes all
- needed code, so you can follow along if you like. The source code is also available in the corresponding
- Github repository.
-