Zakuro-OS Day8
Page Table (Identity Mapping)をUEFIのものから自前のものに置き換えたところ、#PF
で落ちるようになった。デバッグのためPFのハンドラを追加:
fn unhandledFaultHandler(context: *Context) void {
log.err("============ Unhandled Fault ===================", .{});
const cr2 = am.readCr2();
log.err("Fault Address: 0x{X:0>16}", .{cr2});
page.showPageTable(cr2);
log.err("", .{});
log.err("Common unhandled handler continues...", .{});
log.err("", .{});
unhandledHandler(context);
}
ページテーブルを使ったtranslationをダンプするようにした:
/// Show the process of the address translation for the given linear address.
/// TODO: do not use logger of this scope.
pub fn showPageTable(lin_addr: u64) void {
// TODO: remove magic numbers.
const pml4_index = (lin_addr >> 39) & 0x1FF;
const pdp_index = (lin_addr >> 30) & 0x1FF;
const pdt_index = (lin_addr >> 21) & 0x1FF;
const pt_index = (lin_addr >> 12) & 0x1FF;
log.err("Linear Address: 0x{X:0>16} (0x{X}, 0x{X}, 0x{X}, 0x{X})", .{
lin_addr,
pml4_index,
pdp_index,
pdt_index,
pt_index,
});
const cr3 = am.readCr3();
const pml4: [*]Pml4Entry = @ptrFromInt(cr3);
log.debug("PML4: 0x{X:0>16}", .{@intFromPtr(pml4)});
const pml4_entry = pml4[pml4_index];
log.debug("\tPML4[{d}]: 0x{X:0>16}", .{ pml4_index, std.mem.bytesAsValue(u64, &pml4_entry).* });
const pdp: [*]PdptEntry = @ptrFromInt(pml4_entry.phys_pdpt << page_shift);
log.debug("PDPT: 0x{X:0>16}", .{@intFromPtr(pdp)});
const pdp_entry = pdp[pdp_index];
log.debug("\tPDPT[{d}]: 0x{X:0>16}", .{ pdp_index, std.mem.bytesAsValue(u64, &pdp_entry).* });
const pdt: [*]PdtEntry = @ptrFromInt(pdp_entry.phys_pdt << page_shift);
log.debug("PDT: 0x{X:0>16}", .{@intFromPtr(pdt)});
const pdt_entry = pdt[pdt_index];
log.debug("\tPDT[{d}]: 0x{X:0>16}", .{ pdt_index, std.mem.bytesAsValue(u64, &pdt_entry).* });
}
すると、以下のようになっていることが判明:
[INFO ] (main): xHC MMIO base: 0xC000000000
[ERROR] (intr): ============ Unhandled Fault ===================
[ERROR] (intr): Fault Address: 0x000000C000000000
[ERROR] (archp): Linear Address: 0x000000C000000000 (0x1, 0x100, 0x0, 0x0)
[DEBUG] (archp): PML4: 0x00000000001AE000
[DEBUG] (archp): PML4[1]: 0x0000000000000000
本では16GiB空間しかマップしていないものの、xHCのMMIO空間がこの領域を超えていたためフォルトが発生していた模様。
どうせのちのちちゃんとページング処理は本でもやるだろうから、一時的なworkaroundとしてMMIO領域だけを追加でidentity mapすることに:
/// Maps the given virtual address to the physical address identity for 2MiB page.
/// TODO: this is a temporary implementation to workaround the problem
/// that xHC MMIO address exceeds 16GiB.
pub fn mapIdentity(vaddr: u64, allocator: Allocator) !void {
// TODO: remove magic numbers.
const pml4_index = (vaddr >> 39) & 0x1FF;
const pdp_index = (vaddr >> 30) & 0x1FF;
const pdt_index = (vaddr >> 21) & 0x1FF;
const pml4_ent = &pml4_table[pml4_index];
if (!pml4_ent.present) {
const pdpt = try allocator.alloc(PdptEntry, num_table_entries);
pml4_ent.* = Pml4Entry.new(&pdpt[0]);
}
const pdp: [*]PdptEntry = @ptrFromInt(pml4_ent.phys_pdpt << page_shift);
const pdp_ent = &pdp[pdp_index];
if (!pdp_ent.present) {
const pdt = try allocator.alloc(PdtEntry, num_table_entries);
pdp_ent.* = PdptEntry.new(@intFromPtr(pdt.ptr));
}
const pdt: [*]PdtEntry = @ptrFromInt(pdp_ent.phys_pdt << page_shift);
const pdt_ent = &pdt[pdt_index];
if (!pdt_ent.present) {
pdt_ent.* = PdtEntry.new_4mb(
pml4_index * 512 * page_size_1gb + pdp_index * page_size_1gb + pdt_index * page_size_2mb,
);
} else {
// TODO
@panic("The page is already mapped.");
}
}
これで無事に動くようになった。UEFIのページテーブルからの、卒業 (尾崎風)
以下のようなコードを書いたら:
pub fn init() Self {
var self = Self{};
...
return self;
}
この関数中で呼ばれるmemcpy
のあとに.txt
領域が書き換えられてしまった...
0x51125d 48be400a100000.. <mm.BitmapPageAllocator.init+0x1d> movabs rsi, 0x100a40
0x511267 ba10004000 <mm.BitmapPageAllocator.init+0x27> mov edx, 0x400010
-> 0x51126c e8ef650600 <mm.BitmapPageAllocator.init+0x2c> call 0x577860 <compiler_rt.memcpy.memcpy>
0x51126f 0000 <mm.BitmapPageAllocator.init+0x2f> add BYTE PTR [rax], al
-> 0x511271 0000 <mm.BitmapPageAllocator.init+0x31> add BYTE PTR [rax], al
0x511273 0000 <mm.BitmapPageAllocator.init+0x33> add BYTE PTR [rax], al
0x511275 0000 <mm.BitmapPageAllocator.init+0x35> add BYTE PTR [rax], al
これ、多分ページ管理に使うビットマップのサイズが大きすぎてstack overflowしてtxt領域まで書き込みに行ってる気がするな
128GiB管理するためのビットマップだけで4MiB必要になるな。
流石にこれはstack上に確保するんじゃなくてページアロケータから確保したほうが良さそう。ページアロケータの初期化中に、開いてるページに確保してポインタを返すようにする。
また明日。
メモリゲット
Page Allocatorを実装した。
ZigのAllocatorはstdlibのものが珍しくhacky(?)なことをしており、vtableを自前で実装してstd.heap.Allocator
を継承しているかのように見せている。
今回もそれにならって実装した:
/// Instantiate an allocator.
pub fn allocator(self: *Self) Allocator {
return Allocator{
.ptr = self,
.vtable = &.{
.alloc = alloc,
.resize = resize,
.free = free,
},
};
}
これで動的にメモリを確保できるようになったので、今まで.data
にデータを確保してFixedBufferAllocator
を使っていたものを、全部ページアロケータに置き換えた。
これにてDay08終了 🐶