Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Typos and Add Missing Code in Second Half of Kernel Part #11

Merged
merged 17 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a131cb0
fix(src/kernel/page_allocator): correct allocate function name typo
gierens Dec 30, 2024
76a5ddc
fix(src/kernel/general_allocator): correct allocate function name typo
gierens Dec 30, 2024
f3262d5
fix(src/kernel/page_allocator): remove trailing _ from map param
gierens Dec 30, 2024
0535e89
fix(src/kernel/page_allocator): add missing bytes_per_frame definition
gierens Dec 30, 2024
2396ad2
fix(src/kernel/page_allocator): correct allocPages name typo
gierens Dec 30, 2024
b4650f8
fix(src/kernel/page_allocator): complete listings in create allocator
gierens Dec 30, 2024
4cce83a
fix(src/kernel/paging): correct num_table_entries value typo
gierens Dec 30, 2024
5251d80
fix(src/kernel/paging): add more lines to final reconstruct listing
gierens Dec 30, 2024
b6f150e
fix(src/kernel/paging): correct 3-rd typo
gierens Dec 30, 2024
95d0047
fix(src/kernel/paging): correct addr typo in phys2virt
gierens Dec 30, 2024
f0c4d36
fix(src/kernel/paging): add flushTlbSingle function
gierens Jan 2, 2025
936574f
fix(src/kernel/general_allocator): add newUninit, correct mem.zig path
gierens Jan 11, 2025
62cabc8
fix(src/kernel/pic): replace redundant relax definition by asm import
gierens Jan 11, 2025
be51991
fix(src/kernel/pic): add listing of interrupts.zig
gierens Jan 12, 2025
ef8e1a1
fix(src/kernel/pic): correct introduction of interrupts.zig
gierens Jan 18, 2025
6d2b190
fix(src/kernel/panic): correct typo in panic builtin call
gierens Jan 11, 2025
ea1b726
fix(src/kernel/panic): add missing export of panic as panic_fn
gierens Jan 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading