Skip to content

Commit

Permalink
Merge pull request #11 from gierens/fix-typo-3
Browse files Browse the repository at this point in the history
Fix Typos and Add Missing Code in Second Half of Kernel Part
  • Loading branch information
smallkirby authored Jan 18, 2025
2 parents 70af45b + ea1b726 commit 67d2ccd
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 15 deletions.
11 changes: 10 additions & 1 deletion src/kernel/general_allocator.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ Bin から chunk を取得したり返却する処理が実装できたため、

`PageAllocator` の場合と異なり、`BinAllocator` はページサイズ以下の領域を確保する可能性があります。
よって、 `BinAllocator` 要求されたアラインを考慮する必要があります。
要求されるアラインは、`allocater()`の第3引数に渡されます:
要求されるアラインは、`allocate()`の第3引数に渡されます:

```ymir/mem/BinAllocator.zig
fn allocate(ctx: *anyopaque, n: usize, log2_align: u8, _: usize) ?[*]u8 {
Expand Down Expand Up @@ -255,6 +255,15 @@ fn resize(_: *anyopaque, _: []u8, _: u8, _: usize, _: usize) bool {
`BinAllocator` をインスタンス化して利用可能な状態にします:

```ymir/mem/BinAllocator.zig
pub fn newUninit() Self {
return Self{
.page_allocator = undefined,
.list_heads = undefined,
};
}
```

```ymir/mem.zig
pub const general_allocator = Allocator{
.ptr = &bin_allocator_instance,
.vtable = &BinAllocator.vtable,
Expand Down
26 changes: 22 additions & 4 deletions src/kernel/page_allocator.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const PageAllocator = @import("mem/PageAllocator.zig").PageAllocator; // <= 冗
`Allocator` 経由で呼ばれたこれらの関数は、第1引数 `ctx``Allocator.ptr` が渡されます。
これはアロケータインスタンスであるため、共通の `Allocator` 経由で呼ばれても各アロケータの内部実装を呼び出すことができます。
つまりここでやるべきことは、`PageAllocator.zig` にページアロケータの内部実装を定義した上で、
`allocater()` / `free()` / `resize()` の 3API を提供することです。
`allocate()` / `free()` / `resize()` の 3API を提供することです。
この3つさえ実装すれば、残りの細々としたユーティリティ関数は `Allocator` が提供してくれます。

## Bitmap
Expand All @@ -74,7 +74,7 @@ Ymir の `PageAllocator` では、利用できる(割当可能な)ページを
`PageAllocator` では初期化時にこのメモリマップを受取り、メモリを探査して利用可能なページをビットマップに記録していきます:

```ymir/mem/PageAllocator.zig
pub fn init(self: *Self, map_: MemoryMap) void {
pub fn init(self: *Self, map: MemoryMap) void {
var avail_end: Phys = 0;
var desc_iter = MemoryDescriptorIterator.new(map);
Expand Down Expand Up @@ -153,6 +153,7 @@ const BitMap = [num_maplines]MapLineType;

```ymir/mem/PageAllocator.zig
const FrameId = u64;
const bytes_per_frame = 4 * kib;
inline fn phys2frame(phys: Phys) FrameId {
return phys / bytes_per_frame;
Expand Down Expand Up @@ -413,18 +414,31 @@ pub fn allocPages(self: *PageAllocator, num_pages: usize, align_size: usize) ?[]
以上で準備が整いました。
Ymir で利用できる `Allocator` を作成しましょう:

```ymir/mem/PageAllocator.zig
pub fn newUninit() Self {
return Self{
.frame_end = undefined,
.bitmap = undefined,
};
}
```

```ymir/mem.zig
pub const PageAllocator = @import("mem/PageAllocator.zig");
pub var page_allocator_instance = PageAllocator.newUninit();
pub const page_allocator = Allocator{
.ptr = &page_allocator_instance,
.vtable = &PageAllocator.vtable,
};
pub fn initPageAllocator(map: MemoryMap) void {
page_allocator_instance.init(map);
}
```

`page_allocator_instance``PageAllocator` の唯一のインスタンスです。
基本的にこちらのインスタンスは直接触ることはありません。
唯一使う必要があるのは、先ほどの `allocaPages()` を呼び出す場合のみです。
唯一使う必要があるのは、先ほどの `allocPages()` を呼び出す場合のみです。
というか、このインスタンスは直接触らせたくないので本当は `pub` 指定したくありません。
`PageAllocator` という型自体も同様です。
しかし、`Allocator.alignedAlloc()` がページサイズ以上のアラインを許容しないため致し方ありません[^align]
Expand All @@ -435,10 +449,14 @@ pub const page_allocator = Allocator{

利用時には以下のようにして `Allocator` として利用します (内部実装を気にする必要がありません):

```zig
```ymir/main.zig
mem.initPageAllocator(boot_info.memory_map);
log.info("Initialized page allocator", .{});
const page_allocator = ymir.mem.page_allocator;
const array = try page_allocator.alloc(u32, 4);
log.debug("Memory allocated @ {X:0>16}", .{@intFromPtr(array.ptr)});
page_allocator.free(array);
```

## まとめ
Expand Down
25 changes: 20 additions & 5 deletions src/kernel/paging.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ fn allocatePage(allocator: Allocator) PageError![*]align(page_size_4k) u8 {
```ymir/arch/x86/page.zig
pub fn reconstruct(allocator: Allocator) PageError!void {
const lv4tbl_ptr: [*]Lv4Entry = @ptrCast(try allocatePage(allocator));
const lv4tbl = lv4tbl_ptr[0..num_table_entries]; // 256
const lv4tbl = lv4tbl_ptr[0..num_table_entries]; // 512
@memset(lv4tbl, std.mem.zeroes(Lv4Entry));
...
}
```

まず最初に新しい Lv4 ページテーブルを確保します。
`allocatePage()` が返す領域は [many-item pointer](https://ziglang.org/documentation/master/#Pointers) であるため、
テーブルあたりのエントリ数 256 でスライスを作っています。
テーブルあたりのエントリ数 512 でスライスを作っています。
作成したページテーブルはとりあえず全部ゼロ埋めしておきます。
ゼロ埋めすることでエントリの `present` フィールドが 0 になるため、何もマップしない状態になります。
念の為以下にページテーブルエントリの構造を再掲しておきます:
Expand Down Expand Up @@ -245,10 +245,12 @@ fn cloneLevel1Table(lv1_table: []Lv1Entry, allocator: Allocator) PageError![]Lv1
Lv4 ページテーブルのアドレスは CR3 レジスタに書き込むことで変更できます。

```ymir/arch/x86/page.zig
pub fn reconstruct(allocator: Allocator) PageError!void {
...
const cr3 = @intFromPtr(lv4tbl) & ~@as(u64, 0xFFF);
am.loadCr3(cr3);
...
}
```

CR3 へ書き込みを行うと、TLB の前エントリがフラッシュされ古いエントリが無効になります。
Expand All @@ -263,7 +265,7 @@ CR3 へ書き込みを行うと、TLB の前エントリがフラッシュされ

本シリーズの Ymir では [PCID: Process Context Identifiers](https://en.wikipedia.org/wiki/Translation_lookaside_buffer#PCID) は使わないため,この表のフォーマットに従います[^pcid]
表中の `M` は物理アドレスのサイズであり、おそらく最近の Core シリーズでは `46` になるのではないかと思います。
3-th / 4-th bit は Lv4 テーブルにアクセスする際のキャッシュタイプを決定する要因の1つになります。
3-rd / 4-th bit は Lv4 テーブルにアクセスする際のキャッシュタイプを決定する要因の1つになります。
今回は特にこのあたりは気にせず、どちらも `0` としています。

## 仮想-物理アドレス変換
Expand Down Expand Up @@ -304,7 +306,7 @@ pub fn virt2phys(addr: u64) Phys {
pub fn phys2virt(addr: u64) Virt {
return if (!mapping_reconstructed) b: {
// UEFI's page table.
break :b value;
break :b addr;
} else b: {
// Direct map region.
break :b addr + ymir.direct_map_base;
Expand Down Expand Up @@ -386,6 +388,19 @@ pub fn changeMap4k(virt: Virt, attr: PageAttribute) PageError!void {
ただし、Surtr はカーネルのロードで 4KiB だけを使うため、この関数の前提は満たされます。
途中で存在しない (`present == false`) ページエントリが見つかった場合には gracefull にエラーを返すようにしています。

特定の TLB エントリをフラッシュするヘルパー関数は、INVLPG 命令を使用します:

```surtr/arch/x86/asm.zig
pub inline fn flushTlbSingle(virt: u64) void {
asm volatile (
\\invlpg (%[virt])
:
: [virt] "r" (virt),
: "memory"
);
}
```

続いて、カーネルのロード部分を修正します:

```surtr/boot.zig
Expand Down
4 changes: 3 additions & 1 deletion src/kernel/panic.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ pub fn endlessHalt() noreturn {
Zig にはスタックトレースを取得するためのユーティリティ構造体である `StackIterator` があるため今回はこれを使います:

```ymir/panic.zig
pub const panic_fn = panic;
fn panic(msg: []const u8, _: ?*builtin.StackTrace, _: ?usize) noreturn {
...
var it = std.debug.StackIterator.init(@returnAddress(), null);
Expand Down Expand Up @@ -117,7 +119,7 @@ pub const panic = ymir.panic.panic_fn;
`ReleaseFast` レベルだと最適化が結構強く働くため、関数がインライン化されてスタックトレースが出力できない場合があります:

```ymir/main.tmp.zig
panic("fugafuga");
@panic("fugafuga");
```

出力は以下のようになります:
Expand Down
18 changes: 14 additions & 4 deletions src/kernel/pic.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ const Ocw = union(ocw) {
続いて、これらの CW を PIC に対して発行するためのヘルパー関数を定義します:

```ymir/arch/x86/pic.zig
const am = @import("asm.zig");
fn issue(cw: anytype, port: u16) void {
const T = @TypeOf(cw);
if (T != Icw and T != Ocw) {
Expand All @@ -242,10 +244,6 @@ fn issue(cw: anytype, port: u16) void {
}
am.relax();
}
pub fn relax() void {
asm volatile ("rep; nop");
}
```

`issue()``Icw``Ocw` のみを受け付けることを保証しています。
Expand Down Expand Up @@ -491,6 +489,18 @@ pub fn enableInterrupt(port: Ports) void {
}
```

`idefs` は、`interrupts.zig` のインポートです:

```ymir/interrupts.zig
const arch = @import("ymir").arch;
pub const user_intr_base = arch.intr.num_system_exceptions;
pub const pic_timer = 0 + user_intr_base;
...
pub const pic_serial1 = 4 + user_intr_base;
```

なお、IER の 1-th bit をクリアしているのは無限割り込みを防ぐためです。
Tx-empty は、シリアル出力しようとしたデータが実際に送信され、出力バッファに他のデータを書き込める状態になった時に発生します。
しかし、割り込みハンドラの中でさらにシリアルログ出力をしており、これがまた Tx-empty を発生させます。
Expand Down

0 comments on commit 67d2ccd

Please sign in to comment.