Closed5

Zakuro-OS Day8

smallkirbysmallkirby

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のページテーブルからの、卒業 (尾崎風)

smallkirbysmallkirby

以下のようなコードを書いたら:

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領域まで書き込みに行ってる気がするな

smallkirbysmallkirby

128GiB管理するためのビットマップだけで4MiB必要になるな。
流石にこれはstack上に確保するんじゃなくてページアロケータから確保したほうが良さそう。ページアロケータの初期化中に、開いてるページに確保してポインタを返すようにする。

また明日。

smallkirbysmallkirby

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終了 🐶

https://github.com/smallkirby/zakuro-os/commit/7b85a8104959924b7396221e9bfe43fdb29db0f6

このスクラップは2024/06/08にクローズされました