From 2b6694e94821c72f9b6c6c5b549a867d410c999b Mon Sep 17 00:00:00 2001 From: Ratakor Date: Tue, 7 Nov 2023 21:50:45 +0100 Subject: [PATCH] Unfinished changes, WIP vfs and tmpfs --- kernel/arch/x86_64/gdt.zig | 13 +- kernel/arch/x86_64/idt.zig | 2 +- kernel/arch/x86_64/x86_64.zig | 7 +- kernel/fs/tmpfs.zig | 150 +++++++---- kernel/lib/tree.zig | 9 +- kernel/vfs.zig | 480 +++++++++++++++++++++++----------- kernel/vmm.zig | 58 ++-- lib/ubik.zig | 34 +-- 8 files changed, 496 insertions(+), 257 deletions(-) diff --git a/kernel/arch/x86_64/gdt.zig b/kernel/arch/x86_64/gdt.zig index daf2b4e..49288f6 100644 --- a/kernel/arch/x86_64/gdt.zig +++ b/kernel/arch/x86_64/gdt.zig @@ -11,7 +11,7 @@ const GDT = extern struct { user_data: Entry align(1), tss: TSS.Descriptor align(1), - const Entry = packed struct { + const Entry = packed struct(u64) { limit_low: u16 = 0, base_low: u16 = 0, base_mid: u8 = 0, @@ -27,9 +27,7 @@ const GDT = extern struct { }; comptime { - std.debug.assert(@sizeOf(GDT) == 7 * 8); - std.debug.assert(@sizeOf(Entry) == @sizeOf(u64)); - std.debug.assert(@bitSizeOf(Entry) == @bitSizeOf(u64)); + std.debug.assert(@sizeOf(GDT) == 7 * @sizeOf(u64)); std.debug.assert(@bitSizeOf(Descriptor) == 80); } }; @@ -52,7 +50,7 @@ pub const TSS = extern struct { reserved3: u16 align(1) = 0, iopb: u16 align(1), - const Descriptor = packed struct { + const Descriptor = packed struct(u128) { limit_low: u16 = @sizeOf(TSS), base_low: u16 = undefined, base_mid: u8 = undefined, @@ -63,11 +61,6 @@ pub const TSS = extern struct { base_upper: u32 = undefined, reserved: u32 = 0, }; - - comptime { - std.debug.assert(@sizeOf(Descriptor) == @sizeOf(u64) * 2); - std.debug.assert(@bitSizeOf(Descriptor) == @bitSizeOf(u64) * 2); - } }; pub const kernel_code = 0x08; diff --git a/kernel/arch/x86_64/idt.zig b/kernel/arch/x86_64/idt.zig index 739f521..c88f0ed 100644 --- a/kernel/arch/x86_64/idt.zig +++ b/kernel/arch/x86_64/idt.zig @@ -160,7 +160,7 @@ fn defaultHandler(ctx: *Context) callconv(.SysV) void { if (ctx.cs == gdt.user_code) { switch (ctx.vector) { 0x0e => { - if (vmm.handlePageFault(cr2, ctx.error_code)) { + if (vmm.handlePageFault(cr2, @bitCast(@as(u32, @intCast(ctx.error_code))))) { return; } else |err| { log.err("failed to handle page fault: {}", .{err}); diff --git a/kernel/arch/x86_64/x86_64.zig b/kernel/arch/x86_64/x86_64.zig index 26fb0c9..9a9fbc3 100644 --- a/kernel/arch/x86_64/x86_64.zig +++ b/kernel/arch/x86_64/x86_64.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; -pub const RFlags = packed struct { +pub const RFlags = packed struct(u64) { CF: u1 = 0, reserved: u1 = 1, PF: u1 = 0, @@ -23,11 +23,6 @@ pub const RFlags = packed struct { VIP: u1 = 0, ID: u1 = 0, reserved4: u42 = 0, - - comptime { - assert(@sizeOf(RFlags) == @sizeOf(u64)); - assert(@bitSizeOf(RFlags) == @bitSizeOf(u64)); - } }; pub const MSR = enum(u32) { diff --git a/kernel/fs/tmpfs.zig b/kernel/fs/tmpfs.zig index f19d356..ba2ac03 100644 --- a/kernel/fs/tmpfs.zig +++ b/kernel/fs/tmpfs.zig @@ -1,15 +1,18 @@ const std = @import("std"); const root = @import("root"); const vfs = root.vfs; -const page_size = std.mem.page_size; +const vmm = root.vmm; +const pmm = root.pmm; -const file_vtable: vfs.VNode.VTable = .{ +// TODO + +const file_vtable: vfs.Node.VTable = .{ .read = File.read, .write = File.write, .stat = File.stat, }; -const dir_vtable: vfs.VNode.VTable = .{ +const dir_vtable: vfs.Node.VTable = .{ .open = Dir.open, .readDir = Dir.read, .insert = Dir.insert, @@ -23,50 +26,109 @@ const fs_vtable: vfs.FileSystem.VTable = .{ .allocInode = FileSystem.allocInode, }; +const vtable: vfs.Node.VTable = .{ + .read = File.read, + .write = File.write, + .stat = File.stat, + .truncate = File.truncate, +}; + const File = struct { - vnode: vfs.VNode, - data: std.ArrayListAlignedUnmanaged(u8, page_size) = .{}, + node: vfs.Node, + data: std.ArrayListAlignedUnmanaged(u8, blksize) = .{}, - fn read(vnode: *vfs.VNode, buf: []u8, offset: usize, flags: usize) vfs.ReadError!usize { - _ = flags; + const blksize = std.mem.page_size; - const self = @fieldParentPtr(File, "vnode", vnode); + fn read(node: *vfs.Node, buf: []u8, offset: std.os.off_t) vfs.ReadError!usize { + const self = @fieldParentPtr(File, "node", node); if (offset >= self.data.items.len) { return 0; } const bytes_read = @min(buf.len, self.data.items.len - offset); - @memcpy(buf[0..bytes_read], self.data.items[offset .. offset + bytes_read]); + const u_offset: usize = @intCast(offset); + @memcpy(buf[0..bytes_read], self.data.items[u_offset .. u_offset + bytes_read]); return bytes_read; } - fn write(vnode: *vfs.VNode, buf: []const u8, offset: usize, flags: usize) vfs.WriteError!usize { - _ = flags; - - const self = @fieldParentPtr(File, "vnode", vnode); - try self.data.insertSlice(root.allocator, offset, buf); + fn write(node: *vfs.Node, buf: []const u8, offset: std.os.off_t) vfs.WriteError!usize { + const self = @fieldParentPtr(File, "node", node); + try self.data.insertSlice(root.allocator, @intCast(offset), buf); return buf.len; } - fn stat(vnode: *vfs.VNode, buf: *std.os.Stat) vfs.StatError!void { - const self = @fieldParentPtr(File, "vnode", vnode); + fn stat(node: *vfs.Node, buf: *std.os.Stat) vfs.StatError!void { + const self = @fieldParentPtr(File, "node", node); + buf.* = .{ + .dev = undefined, + .ino = node.inode, + .mode = node.mode, // TODO: 0o777 | std.os.S.IFREG, + .nlink = node.nlink, + .uid = node.uid, + .gid = node.gid, + .rdev = undefined, + .size = @intCast(self.data.items.len), + .blksize = blksize, + .blocks = std.math.divCeil(usize, self.data.items.len, blksize), // TODO: use @divCeil + .atim = node.atim, + .mtim = node.mtim, + .ctim = node.ctim, + }; + } - buf.* = std.mem.zeroes(std.os.Stat); - buf.ino = vnode.inode; - buf.mode = 0o777 | std.os.S.IFREG; // TODO - buf.size = @intCast(self.data.items.len); - buf.blksize = page_size; - buf.blocks = @intCast(std.mem.alignForward(usize, self.data.items.len, page_size) / page_size); + // TODO + fn mmap(node: *vfs.Node, file_page: usize, flags: u64) !*anyopaque { + const self = @fieldParentPtr(File, "node", node); + return if (flags & std.os.MAP.SHARED != 0) + @intFromPtr(self.data[file_page * blksize ..].ptr) - vmm.hhdm_offset + else blk: { + const data = try root.allocator.alloc(u8, blksize); + @memcpy(data, self.data[file_page * blksize ..][0..blksize]); + break :blk @intFromPtr(data.ptr) - vmm.hhdm_offset; + }; + } + + fn truncate(node: *vfs.Node, length: usize) vfs.DefaultError!void { + const self = @fieldParentPtr(File, "node", node); + try self.data.resize(root.allocator, length); + } + + fn create(parent: *vfs.Node, name: []const u8, mode: std.os.mode_t) vfs.CreateError!void { + _ = mode; + _ = name; + _ = parent; + const node = try root.allocator.create(vfs.Node); + node.* = .{ + .vtable = &vtable, + }; + return node; + // TODO } + + // TODO: create_resource + // TODO: instantiate? + // TODO: mount + // TODO: create + // TODO: symlink + // TODO: link }; +// pub const MountFn = *const fn (arg: []const u8, mount_point: []const u8) *Node; +fn mount(parent: *vfs.Node, name: []const u8, _: *vfs.Node) *vfs.Node { + _ = name; + _ = parent; + // return parent.createFile(parent, + // TODO + +} + const Dir = struct { - vnode: vfs.VNode, - children: std.ArrayListUnmanaged(*vfs.VNode) = .{}, + node: vfs.Node, + children: std.ArrayListUnmanaged(*vfs.Node) = .{}, - fn open(vnode: *vfs.VNode, name: []const u8, flags: usize) vfs.OpenError!*vfs.VNode { + fn open(node: *vfs.Node, name: []const u8, flags: u64) vfs.OpenError!*vfs.Node { _ = flags; - const self = @fieldParentPtr(Dir, "vnode", vnode); + const self = @fieldParentPtr(Dir, "node", node); for (self.children.items) |child| { if (std.mem.eql(u8, child.name, name)) { return child; @@ -77,8 +139,8 @@ const Dir = struct { } // TODO - fn read(vnode: *vfs.VNode, buf: []u8, offset: *usize) vfs.ReadDirError!usize { - const self = @fieldParentPtr(Dir, "vnode", vnode); + fn read(node: *vfs.Node, buf: []u8, offset: *usize) vfs.ReadDirError!usize { + const self = @fieldParentPtr(Dir, "node", node); var dir_ent: *std.os.system.DirectoryEntry = @ptrCast(@alignCast(buf.ptr)); var buf_offset: usize = 0; @@ -90,7 +152,7 @@ const Dir = struct { if (buf_offset + real_size > buf.len) break; dir_ent.d_off = 0; - dir_ent.d_ino = vnode.inode; + dir_ent.d_ino = node.inode; dir_ent.d_reclen = @truncate(real_size); dir_ent.d_type = return @intFromEnum(child.kind); @memcpy(dir_ent.d_name[0..child.name.len], child.name); // TODO @@ -102,8 +164,8 @@ const Dir = struct { return buf_offset; } - fn insert(vnode: *vfs.VNode, new_child: *vfs.VNode) vfs.InsertError!void { - const self = @fieldParentPtr(Dir, "vnode", vnode); + fn insert(node: *vfs.Node, new_child: *vfs.Node) vfs.InsertError!void { + const self = @fieldParentPtr(Dir, "node", node); for (self.children.items) |child| { if (std.mem.eql(u8, child.name, new_child.name)) { return error.PathAlreadyExists; @@ -112,9 +174,9 @@ const Dir = struct { try self.children.append(root.allocator, new_child); } - fn stat(vnode: *vfs.VNode, buf: *std.os.Stat) vfs.StatError!void { + fn stat(node: *vfs.Node, buf: *std.os.Stat) vfs.StatError!void { buf.* = std.mem.zeroes(std.os.Stat); - buf.ino = vnode.inode; + buf.ino = node.inode; buf.mode = 0o777 | std.os.S.IFDIR; // TODO } }; @@ -124,12 +186,12 @@ const FileSystem = struct { root: Dir, inode_counter: u64, - fn createFile(fs: *vfs.FileSystem) !*vfs.VNode { + fn createFile(fs: *vfs.FileSystem) !*vfs.Node { const file = try root.allocator.create(File); // TODO!: are data field, parent or kind field correctly put in file? file.* = .{ - .vnode = .{ + .node = .{ .vtable = &file_vtable, .filesystem = fs, .kind = .file, @@ -138,14 +200,14 @@ const FileSystem = struct { }, }; - return &file.vnode; + return &file.node; } - fn createDir(fs: *vfs.FileSystem) !*vfs.VNode { + fn createDir(fs: *vfs.FileSystem) !*vfs.Node { const dir = try root.allocator.create(Dir); dir.* = .{ - .vnode = .{ + .node = .{ .vtable = &dir_vtable, .filesystem = fs, .kind = .directory, @@ -154,11 +216,11 @@ const FileSystem = struct { }, }; - return &dir.vnode; + return &dir.node; } - fn createSymlink(fs: *vfs.FileSystem, target: []const u8) !*vfs.VNode { - const symlink = try root.allocator.create(vfs.VNode); + fn createSymlink(fs: *vfs.FileSystem, target: []const u8) !*vfs.Node { + const symlink = try root.allocator.create(vfs.Node); errdefer root.allocator.destroy(symlink); symlink.* = .{ @@ -180,7 +242,7 @@ const FileSystem = struct { } }; -pub fn init(name: []const u8, parent: ?*vfs.VNode) !*vfs.VNode { +pub fn init(name: []const u8, parent: ?*vfs.Node) !*vfs.Node { const fs = try root.allocator.create(FileSystem); fs.* = .{ @@ -188,7 +250,7 @@ pub fn init(name: []const u8, parent: ?*vfs.VNode) !*vfs.VNode { .vtable = &fs_vtable, }, .root = .{ - .vnode = .{ + .node = .{ .vtable = &dir_vtable, .filesystem = &fs.filesystem, .kind = .directory, @@ -200,5 +262,5 @@ pub fn init(name: []const u8, parent: ?*vfs.VNode) !*vfs.VNode { .inode_counter = 1, }; - return &fs.root.vnode; + return &fs.root.node; } diff --git a/kernel/lib/tree.zig b/kernel/lib/tree.zig index 2c02711..063e00d 100644 --- a/kernel/lib/tree.zig +++ b/kernel/lib/tree.zig @@ -1,3 +1,5 @@ +//! DEPRECATED (and unfinished) + const std = @import("std"); const Allocator = std.mem.Allocator; @@ -14,14 +16,13 @@ pub fn Tree(comptime T: type) type { return node; } - // TODO: add a comptime bool for if to free value? - /// Free a node, its children and their values - pub fn deinit(self: *Node, allocator: Allocator) void { + /// Free a node, its children and optionaly its value + pub fn deinit(self: *Node, allocator: Allocator, comptime free_value: bool) void { for (self.children.items) |child| { child.deinit(allocator); } - if (comptime @typeInfo(T) == .Pointer) { + if (comptime free_value and @typeInfo(T) == .Pointer) { switch (comptime @typeInfo(T).Size) { .One => allocator.destroy(self.value), .Slice => allocator.free(self.value), diff --git a/kernel/vfs.zig b/kernel/vfs.zig index 1a83c20..189e262 100644 --- a/kernel/vfs.zig +++ b/kernel/vfs.zig @@ -3,46 +3,43 @@ const os = std.os; const root = @import("root"); const time = @import("time.zig"); const SpinLock = root.SpinLock; -const Tree = root.Tree(*Entry); const DirectoryEntry = root.os.system.DirectoryEntry; const log = std.log.scoped(.vfs); // TODO: not sure about these errors -const AllocatorError = std.mem.Allocator.Error; -pub const OpenError = os.OpenError || AllocatorError; -pub const ReadError = os.ReadError || AllocatorError; +pub const DefaultError = std.mem.Allocator.Error || os.UnexpectedError; +pub const OpenError = os.OpenError || DefaultError; +pub const ReadError = os.ReadError || DefaultError; pub const ReadDirError = ReadError || error{IsNotDir}; pub const ReadLinkError = ReadError || error{IsNotLink}; -pub const WriteError = os.WriteError || AllocatorError; -pub const CreateError = os.MakeDirError || AllocatorError; -pub const IoctlError = AllocatorError || os.UnexpectedError; // TODO -pub const StatError = os.FStatAtError || AllocatorError; +pub const WriteError = os.WriteError || DefaultError; +pub const CreateError = os.MakeDirError || DefaultError; +pub const StatError = os.FStatError || DefaultError; -// TODO: Node as function with device as T? and funcs? -> rename INode +// TODO: rename VNode? pub const Node = struct { - vtable: *const VTable = &.{}, - name: []const u8, + vtable: *const VTable, + name: []u8, kind: Kind, - device: ?*anyopaque = null, // TODO: context, in stat? - stat: std.os.Stat = .{ - .dev = undefined, - .ino = undefined, - .rdev = undefined, - .blksize = undefined, - }, - filesystem: u64 = 0, // TODO: ptr - open_flags: u64 = 0, // TODO: read/write/append, ... - - mountpoint: ?*Node = null, // TODO: symlinks - refcount: isize = 0, //std.atomic.Atomic(isize), // TODO: usize + dev_id: u64, // device + inode: os.ino_t, // file serial number + mode: os.mode_t, // file mode + nlink: os.nlink_t, // hard link count + uid: os.uid_t, // owner user id + gid: os.gid_t, // owner group id + atim: os.timespec, // time of last access + mtim: os.timespec, // time of last modification + ctim: os.timespec, // time of last status change + open_flags: u64, // TODO: read/write/append, ... + mount_point: ?*Node, // TODO: symlinks <- no it's a different thing + refcount: usize, lock: SpinLock = .{}, // TODO: use // can_mmap: bool, // TODO // status: i32, // TODO // event: Event, // TODO - // filesystem <- in vtable - - // parent and children in Tree.Node - // TODO: in Tree.Node change children to StringHashMapUnmanaged with filename as key? + filesystem: ?*const FileSystem, // TODO + nullable? + parent: ?*Node, // TODO: nullable? + children: std.StringHashMapUnmanaged(*Node) = .{}, pub const VTable = struct { open: *const fn (self: *Node, flags: u64) OpenError!void = @ptrCast(&stubFn), @@ -51,21 +48,23 @@ pub const Node = struct { write: *const fn (self: *Node, buf: []const u8, offset: os.off_t) WriteError!usize = @ptrCast(&stubFn), readLink: *const fn (self: *Node, buf: []u8) ReadLinkError!usize = @ptrCast(&stubFn), readDir: *const fn (self: *Node, index: usize) ReadDirError!*DirectoryEntry = @ptrCast(&stubFn), - findDir: *const fn (self: *Node, name: []const u8) error{}!*Node = @ptrCast(&stubFn), // TODO: error + findDir: *const fn (self: *Node, name: []const u8) DefaultError!*Node = @ptrCast(&stubFn), // TODO: error + ioctl: *const fn (self: *Node, request: u64, arg: *anyopaque) DefaultError!u64 = @ptrCast(&stubFn), // return u64? + chmod: *const fn (self: *Node, mode: os.mode_t) DefaultError!void = &stubChmod, // @ptrCast(&stubFn), // TODO: error + chown: *const fn (self: *Node, uid: os.uid_t, gid: os.gid_t) DefaultError!void = @ptrCast(&stubFn), // TODO: error + truncate: *const fn (self: *Node, length: usize) DefaultError!void = @ptrCast(&stubFn), // TODO: error + unlink: *const fn (self: *Node, name: []const u8) DefaultError!void = @ptrCast(&stubFn), // TODO: error + stat: *const fn (self: *Node, statbuf: *os.Stat) StatError!void = @ptrCast(&stubFn), + + // TODO: filesystem // TODO: merge createFile and makeDir? createFile: *const fn (parent: *Node, name: []const u8, mode: os.mode_t) CreateError!void = @ptrCast(&stubFn), makeDir: *const fn (parent: *Node, name: []const u8, mode: os.mode_t) CreateError!void = @ptrCast(&stubFn), - symLink: *const fn (parent: *Node, name: []const u8, target: []const u8) CreateError!void = @ptrCast(&stubFn), - ioctl: *const fn (self: *Node, request: u64, arg: *anyopaque) IoctlError!u64 = @ptrCast(&stubFn), // return u64? - chmod: *const fn (self: *Node, mode: os.mode_t) error{}!void = &stubChmod, // @ptrCast(&stubFn), // TODO: error - chown: *const fn (self: *Node, uid: os.uid_t, gid: os.gid_t) error{}!void = @ptrCast(&stubFn), - truncate: *const fn (self: *Node) error{}!void = @ptrCast(&stubFn), // TODO: add length to args + error - unlink: *const fn (self: *Node, name: []const u8) error{}!void = @ptrCast(&stubFn), // TODO: error - stat: *const fn (self: *Node, buf: *os.Stat) StatError!void = @ptrCast(&stubFn), - - fn stubChmod(self: *Node, mode: os.mode_t) error{}!void { - self.stat.mode &= ~@as(os.mode_t, 0o777); - self.stat.mode |= mode & 0o777; + symlink: *const fn (parent: *Node, name: []const u8, target: []const u8) CreateError!void = @ptrCast(&stubFn), + + fn stubChmod(self: *Node, mode: os.mode_t) DefaultError!void { + self.mode &= ~@as(os.mode_t, 0o777); + self.mode |= mode & 0o777; } fn stubFn() !void { @@ -73,47 +72,128 @@ pub const Node = struct { } }; - /// https://en.wikipedia.org/wiki/Unix_file_types pub const Kind = enum { block_device, character_device, directory, named_pipe, - sym_link, + symlink, file, unix_domain_socket, whiteout, - door, - event_port, unknown, }; - var refcount_lock: SpinLock = .{}; + // TODO: is DT actually used + // pub const Kind = enum(u64) { // TODO: u64? + // block_device = os.DT.BLK, + // character_device = os.DT.CHR, + // directory = os.DT.DIR, + // named_pipe = os.DT.FIFO, + // symlink = os.DT.LNK, + // file = os.DT.REG, + // unix_domain_socket = os.DT.SOCK, + // whiteout = os.DT.WHT, + // unknown = os.DT.UNKNOWN, + // }; + + pub const Stream = struct { + node: *Node, + offset: u64 = 0, + + pub const SeekError = error{}; + pub const GetSeekPosError = error{}; + + pub const SeekableStream = std.io.SeekableStream( + *Stream, + SeekError, + GetSeekPosError, + Stream.seekTo, + Stream.seekBy, + Stream.getPosFn, + Stream.getEndPosFn, + ); + + pub const Reader = std.io.Reader(*Stream, ReadError, Stream.read); + + fn seekTo(self: *Stream, offset: u64) SeekError!void { + self.offset = offset; + } + + fn seekBy(self: *Stream, offset: i64) SeekError!void { + self.offset +%= @bitCast(offset); + } + + fn getPosFn(self: *Stream) GetSeekPosError!u64 { + return self.offset; + } + + fn getEndPosFn(self: *Stream) GetSeekPosError!u64 { + _ = self; + return 0; // TODO + } - // TODO: use a spin lock instead lol - pub fn lock(self: *Node) void { - refcount_lock.lock(); - self.refcount = -1; - refcount_lock.unlock(); + fn read(self: *Stream, buf: []u8) ReadError!usize { + return self.node.read(buf, self.offset); + } + + pub fn seekableStream(self: *Stream) SeekableStream { + return .{ .context = self }; + } + + pub fn reader(self: *Stream) Reader { + return .{ .context = self }; + } + }; + + pub fn init(name: []const u8, fs: ?*const FileSystem, parent: ?*Node) !*Node { + const node = try root.allocator.create(Node); + node.* = .{ + .vtable = &.{}, + .name = try root.allocator.dupe(u8, name), + .kind = undefined, + .dev_id = undefined, + .inode = undefined, + .mode = 0o666, + .nlink = 0, + .uid = 0, + .gid = 0, + .atim = time.realtime, + .mtim = time.realtime, + .ctim = time.realtime, + .open_flags = 0, + .mount_point = null, + .refcount = 0, + .filesystem = fs, + .parent = parent, + }; + return node; + } + + pub fn getEffectiveNode(self: *Node, follow_symlinks: bool) *Node { + // if (self.redirection) |redirection| { + // return getEffectiveNode(redirection, follow_symlinks); + // } + if (self.mount_point) |mount_point| { + return getEffectiveNode(mount_point, follow_symlinks); + } + if (follow_symlinks) { + if (self.symlink_target) |symlink_target| { + _ = symlink_target; + // TODO + } + } + return self; } // TODO pub fn open(self: *Node, flags: u64) OpenError!*Node { - if (self.refcount >= 0) { - refcount_lock.lock(); - self.refcount += 1; - refcount_lock.unlock(); - } + self.refcount += 1; return self.vtable.open(self, flags); } // TODO pub fn close(self: *Node) void { - if (self.refcount == -1) return; - - refcount_lock.lock(); - defer refcount_lock.unlock(); - self.refcount -= 1; if (self.refcount == 0) { self.vtable.close(self); @@ -132,7 +212,7 @@ pub const Node = struct { } pub inline fn readLink(self: *Node, buf: []u8) ReadLinkError!void { - if (self.kind != .sym_link) return error.IsNotLink; + if (self.kind != .symlink) return error.IsNotLink; return self.vtable.readLink(self, buf); } @@ -141,60 +221,67 @@ pub const Node = struct { return self.vtable.readDir(self, index); } + // TODO pub inline fn findDir(self: *Node, name: []const u8) !*Node { if (self.kind != .directory) return error.IsNotDir; return self.vtable.findDir(self, name); } - pub inline fn ioctl(self: *Node, request: u64, arg: *anyopaque) IoctlError!u64 { + pub inline fn ioctl(self: *Node, request: u64, arg: *anyopaque) DefaultError!u64 { return self.vtable.ioctl(self, request, arg); } - pub inline fn chmod(self: *Node, mode: os.mode_t) !void { + pub inline fn chmod(self: *Node, mode: os.mode_t) DefaultError!void { return self.vtable.chmod(self, mode); } - pub inline fn chown(self: *Node, uid: os.uid_t, gid: os.gid_t) !void { + pub inline fn chown(self: *Node, uid: os.uid_t, gid: os.gid_t) DefaultError!void { return self.vtable.chown(self, uid, gid); } - pub inline fn truncate(self: *Node) !void { + pub inline fn truncate(self: *Node) DefaultError!void { return self.vtable.truncate(self); } - - pub inline fn toTreeNode(self: *Node) *Tree.Node { - std.debug.assert(tree.find(self)); - return @fieldParentPtr(Tree.Node, "value", self); - } }; -pub const Entry = struct { - name: []u8, // TODO: const? - file: ?*Node = null, - device: ?[]u8 = null, // TODO - fs_type: ?[]u8 = null, // TODO +// TODO +pub const FileSystem = struct { + vtable: *const VTable, + vnode_covered: *Node, // vnode we cover + flags: i32, // flags + bsize: i32, // native block size + + pub const VTable = struct { + mount: *const fn () void, + unmount: *const fn () void, + root: *const fn () void, + statfs: *const fn () void, + sync: *const fn () void, + fid: *const fn () void, + vget: *const fn () void, + }; }; pub const FileDescriptor = struct { node: *Node, offset: os.off_t, - mode: os.mode_t, // TODO: mode, lock, flags, refcount? }; -pub const MountFn = *const fn (arg: []const u8, mount_point: []const u8) *Node; +pub const MountFn = *const fn (parent: *Node, target: []const u8, source: *Node) CreateError!*Node; var filesystems: std.StringHashMapUnmanaged(MountFn) = .{}; -var tree = Tree.init(root.allocator); +var root_node: *Node = undefined; var vfs_lock: SpinLock = .{}; pub fn init() void { - const root_node = root.allocator.create(Entry) catch unreachable; - root_node.* = .{ .name = root.allocator.dupe(u8, "root") catch unreachable }; - tree.setRoot(root_node) catch unreachable; + root_node = Node.init("", null, null) catch unreachable; } pub fn registerFileSystem(name: []const u8, mountFn: MountFn) !void { + vfs_lock.lock(); + defer vfs_lock.unlock(); + const rv = try filesystems.getOrPut(root.allocator, name); std.debug.assert(rv.found_existing == false); rv.value_ptr.* = mountFn; @@ -208,7 +295,7 @@ pub fn createFile(name: []const u8, mode: os.mode_t, comptime is_dir: bool) !voi // TODO } -pub fn symLink(name: []const u8, target: []const u8) !void { +pub fn symlink(name: []const u8, target: []const u8) !void { _ = target; _ = name; // TODO @@ -219,90 +306,181 @@ pub fn unlink(name: []const u8) !void { // TODO } -pub fn mount(path: []const u8, file: *Node) !*Node { - std.debug.assert(std.fs.path.isAbsolute(path)); - - vfs_lock.lock(); - defer vfs_lock.unlock(); - - file.refcount = -1; +fn makePath(path: []const u8, fs_name: []const u8) !*Node { + if (!std.fs.path.isAbsolute(path)) return error.PathIsNotAbsolute; + const fs = filesystems.get(fs_name) orelse return error.UnknownFileSystem; + _ = fs; - var node = tree.root.?; + var node = root_node; var iter = std.mem.tokenizeScalar(u8, path, std.fs.path.sep); - - // TODO: this is makePath, extract it? while (iter.next()) |component| { - for (node.children.items) |child| { - const entry = child.value; - if (std.mem.eql(u8, entry.name, component)) { - node = child; - break; - } + const gop = try node.children.getOrPut(root.allocator, component); + if (gop.found_existing) { + node = gop.value_ptr.*; } else { - // component not found in node.children so we create it - const entry = try root.allocator.create(Entry); - entry.* = .{ .name = try root.allocator.dupe(u8, component) }; - node = try tree.insert(node, entry); + const new_node = try Node.init(); // TODO fs.create + gop.value_ptr.* = new_node; + node = new_node; } } - - const entry = node.value; - - if (entry.file) |_| { - log.warn("path {s} is already mounted!", .{path}); - // TODO: return or throw err? - } - entry.file = file; - - log.info("mounted `{s}` to `{s}`", .{ file.name, path }); - return node; } -// TODO -fn readDirMapper(self: *Node, index: usize) ReadDirError!*DirectoryEntry { - if (self.device) |device| { - const dev: *Tree.Node = @ptrCast(device); - - if (index == 0) { - const dirent = try root.allocator.create(DirectoryEntry); - @memcpy(dirent.name[0..], "."); - dirent.ino = 0; - return dirent; - } else if (index == 1) { - const dirent = try root.allocator.create(DirectoryEntry); - @memcpy(dirent.name[0..], ".."); - dirent.ino = 1; - return dirent; - } +// TODO: rework +const Path2Node = struct { + target_parent: *Node, + target: ?*Node, + basename: []const u8, + + pub fn init(parent: *Node, path: []const u8) !Path2Node { + const ask_for_dir = path[path.len - 1] == std.fs.path.sep; // TODO? + + var current_node = if (path[0] == std.fs.path.sep) + root_node.getEffectiveNode(false) + else + // TODO: populate + parent.getEffectiveNode(false); + + var iter = std.mem.tokenizeScalar(u8, path, std.fs.path.sep); + while (iter.next()) |component| { + current_node = current_node.getEffectiveNode(false); + var next_node = current_node.children.get(component) orelse { + _ = iter.peek() orelse return .{ + .target_parent = current_node, + .target = null, + .basename = try root.allocator.dupe(u8, component), + }; + return error.NodeNotFound; + }; + next_node = next_node.getEffectiveNode(false); + // TODO: populate + + _ = iter.peek() orelse { + if (ask_for_dir and !os.S.ISDIR(next_node.mode)) { + return .{ + .target_parent = current_node, + .target = null, + .basename = try root.allocator.dupe(u8, component), + }; + } + return .{ + .target_parent = current_node, + .target = next_node, + .basename = try root.allocator.dupe(u8, component), + }; + }; + + current_node = next_node; + + if (os.S.ISLNK(current_node.mode)) { + // const r = try Path2Node.init(current_node.parent, current_node.symlink_target); + // if (r.target) |target| { + // current_node = target; + // } else { + // return error.bad; // TODO + // } + } - index -= 2; - var i: usize = 0; - for (dev.children.items) |child| { - if (i == index) { - const entry = child.value; - const dirent = try root.allocator.create(DirectoryEntry); - @memcpy(dirent, entry.name); // TODO - dirent.ino = i; - return dirent; + if (!os.S.ISDIR(current_node.mode)) { + return error.IsDir; } - i += 1; } + unreachable; } +}; - return null; +pub fn mount(parent: *Node, source: []const u8, target: []const u8, fs_name: []const u8) !void { + _ = fs_name; + _ = target; + _ = source; + _ = parent; + vfs_lock.lock(); + defer vfs_lock.unlock(); + + // TODO } +// pub fn mount(path: []const u8, file: *Node) !*Node { +// std.debug.assert(std.fs.path.isAbsolute(path)); +// std.debug.assert(file.mountpoint == null); // TODO + +// vfs_lock.lock(); +// defer vfs_lock.unlock(); + +// file.refcount = -1; + +// var node = root_node; +// var iter = std.mem.tokenizeScalar(u8, path, std.fs.path.sep); + +// // TODO: this is makePath, extract it? +// while (iter.next()) |component| { +// const gop = try node.children.getOrPut(root.allocator, component); +// if (gop.found_existing) { +// node = gop.value_ptr.*; +// } else { +// const new_node = try root.allocator.create(Node); +// // const new_node = try Node.init(); // TODO +// gop.value_ptr.* = new_node; +// node = new_node; +// } +// } + +// // TODO: mountpoint? +// // if (node.mountpoint) |_| { +// // log.warn("path {s} is already mounted!", .{path}); +// // // TODO: return or throw err? +// // } +// file.mountpoint = node; + +// log.info("mounted `{s}` to `{s}`", .{ file.name, path }); + +// return node; +// } + // TODO -fn mapper() !*Node { - const node = try root.allocator.create(Node); - node.* = .{ - .vtable = .{ .readDir = readDirMapper }, - .mode = 0o555, - .kind = .directory, - .ctime = time.now(), - .mtime = time.now(), - .atime = time.now(), - }; - return node; -} +// fn readDirMapper(self: *Node, index: usize) ReadDirError!*DirectoryEntry { +// if (self.device) |device| { +// const dev: *Tree.Node = @ptrCast(device); + +// if (index == 0) { +// const dirent = try root.allocator.create(DirectoryEntry); +// @memcpy(dirent.name[0..], "."); +// dirent.ino = 0; +// return dirent; +// } else if (index == 1) { +// const dirent = try root.allocator.create(DirectoryEntry); +// @memcpy(dirent.name[0..], ".."); +// dirent.ino = 1; +// return dirent; +// } + +// index -= 2; +// var i: usize = 0; +// for (dev.children.items) |child| { +// if (i == index) { +// const entry = child.value; +// const dirent = try root.allocator.create(DirectoryEntry); +// @memcpy(dirent, entry.name); // TODO +// dirent.ino = i; +// return dirent; +// } +// i += 1; +// } +// } + +// return null; +// } + +// // TODO +// fn mapper() !*Node { +// const node = try root.allocator.create(Node); +// node.* = .{ +// .vtable = .{ .readDir = readDirMapper }, +// .mode = 0o555, +// .kind = .directory, +// .ctime = time.now(), +// .mtime = time.now(), +// .atime = time.now(), +// }; +// return node; +// } diff --git a/kernel/vmm.zig b/kernel/vmm.zig index 504d720..bdfccc6 100644 --- a/kernel/vmm.zig +++ b/kernel/vmm.zig @@ -26,23 +26,23 @@ pub const MapError = error{ }; /// Page Table Entry -pub const PTE = packed struct { - present: u1, - writable: u1, - user: u1, - write_through: u1, - cache_disable: u1, - accessed: u1, - dirty: u1, +pub const PTE = packed struct(u64) { + present: bool, + writable: bool, + user: bool, + write_through: bool, + cache_disable: bool, + accessed: bool, + dirty: bool, /// page attribute table - pat: u1, - global: u1, + pat: bool, + global: bool, ignored1: u3, /// between u24 and u39 based on MAXPHYADDR from cpuid, the rest must be 0s address: u40, ignored2: u7, protection_key: u4, - execute_disable: u1, + execute_disable: bool, const present: u64 = 1 << 0; const writable: u64 = 1 << 1; @@ -58,7 +58,7 @@ pub const PTE = packed struct { } inline fn getNextLevel(self: *PTE, allocate: bool) ?[*]PTE { - if (self.present != 0) { + if (self.present) { return @ptrFromInt(self.getAddress() + hhdm_offset); } @@ -70,13 +70,22 @@ pub const PTE = packed struct { self.* = @bitCast(new_page_table | present | writable | user); return @ptrFromInt(new_page_table + hhdm_offset); } +}; - comptime { - std.debug.assert(@sizeOf(PTE) == @sizeOf(u64)); - std.debug.assert(@bitSizeOf(PTE) == @bitSizeOf(u64)); - } +pub const PageFaultError = packed struct(u32) { + present: bool, + write: bool, + user: bool, + reserved_write: bool, + instruction_fetch: bool, + protection_key: bool, + shadow_stack: bool, + reserved1: u8, + software_guard_extensions: bool, + reserved2: u16, }; +// TODO: replace // TODO: init and deinit func? const MMapRangeGlobal = struct { shadow_addr_space: *AddressSpace, @@ -87,6 +96,7 @@ const MMapRangeGlobal = struct { offset: std.os.off_t, }; +// TODO: replace // TODO: init and deinit func? const MMapRangeLocal = struct { addr_space: *AddressSpace, @@ -98,6 +108,7 @@ const MMapRangeLocal = struct { flags: i32, }; +// TODO: improve const Addr2Range = struct { range: *MMapRangeLocal, memory_page: usize, @@ -214,7 +225,7 @@ pub const AddressSpace = struct { var i = local_range.base; while (i < local_range.base + local_range.length) : (i += page_size) { const old_pte = self.virt2pte(i, false) orelse unreachable; // TODO: continue? - if (old_pte.present == 0) continue; + if (old_pte.present == false) continue; const new_pte = new_addr_space.virt2pte(i, true) orelse unreachable; // TODO: free const new_spte = new_global_range.shadow_addr_space.virt2pte(i, true) orelse unreachable; // TODO: free @@ -248,7 +259,7 @@ pub const AddressSpace = struct { pub fn virt2phys(self: *const AddressSpace, vaddr: u64) MapError!u64 { const pte = self.virt2pte(vaddr, false) orelse unreachable; - if (pte.present == 0) return error.NotMapped; + if (!pte.present) return error.NotMapped; return pte.getAddress(); } @@ -258,7 +269,7 @@ pub const AddressSpace = struct { // TODO: when virt2pte fails the memory it allocated is not freed const pte = self.virt2pte(vaddr, true) orelse return error.OutOfMemory; - if (pte.present != 0) return error.AlreadyMapped; + if (pte.present) return error.AlreadyMapped; pte.* = @bitCast(paddr | flags); self.flush(vaddr); } @@ -268,7 +279,7 @@ pub const AddressSpace = struct { defer self.lock.unlock(); const pte = self.virt2pte(vaddr, false) orelse unreachable; // TODO: unreachable? - if (pte.present == 0) return error.NotMapped; + if (!pte.present) return error.NotMapped; pte.* = @bitCast(pte.getAddress() | flags); self.flush(vaddr); } @@ -278,7 +289,7 @@ pub const AddressSpace = struct { defer if (lock) self.lock.unlock(); const pte = self.virt2pte(vaddr, false) orelse unreachable; // TODO: unreachable? - if (pte.present == 0) return error.NotMapped; + if (!pte.present) return error.NotMapped; pte.* = @bitCast(@as(u64, 0)); self.flush(vaddr); } @@ -495,9 +506,8 @@ pub inline fn switchPageTable(page_table: u64) void { arch.writeRegister("cr3", page_table); } -pub fn handlePageFault(cr2: u64, reason: u64) !void { - if (reason & 0b0001 != 0) return error.MapIsPresent; // page protection violation - // if (reason & 0b0010 != 0) "write access" else "read access"; +pub fn handlePageFault(cr2: u64, reason: PageFaultError) !void { + if (reason.present) return error.PageProtectionViolation; const addr_space = sched.currentThread().process.addr_space; addr_space.lock.lock(); diff --git a/lib/ubik.zig b/lib/ubik.zig index 06efdc2..7c29809 100644 --- a/lib/ubik.zig +++ b/lib/ubik.zig @@ -23,42 +23,42 @@ pub const nlink_t = linux.nlink_t; pub const blksize_t = linux.blksize_t; pub const blkcnt_t = linux.blkcnt_t; -// TODO: change undefined initializers +// TODO: initializers pub const Stat = extern struct { /// device - dev: dev_t = undefined, + dev: dev_t, /// file serial number - ino: ino_t = undefined, + ino: ino_t, /// file mode - mode: mode_t = 0o666, + mode: mode_t, /// hard link count - nlink: nlink_t = 0, + nlink: nlink_t, /// user id of the owner - uid: uid_t = 0, + uid: uid_t, /// group id of the owner - gid: gid_t = 0, + gid: gid_t, /// device number, if device - rdev: dev_t = undefined, + rdev: dev_t, /// size of file in bytes - size: off_t = 0, + size: off_t, /// optimal block size for I/O - blksize: blksize_t = undefined, + blksize: blksize_t, /// number of 512-byte block allocated - blocks: blkcnt_t = 0, + blocks: blkcnt_t, /// time of last access - atim: timespec = .{}, + atim: timespec, /// time of last modification - mtim: timespec = .{}, + mtim: timespec, /// time of last status change - ctim: timespec = .{}, + ctim: timespec, }; // TODO: initializers pub const DirectoryEntry = extern struct { ino: ino_t, - off: off_t, - reclen: u16, - type: u8, + off: off_t, // use? + reclen: u16, // use? + type: u8, // use? name: [255:0]u8 = undefined, };