Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion disk/.gitkeep

This file was deleted.

1 change: 1 addition & 0 deletions disk/hello.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Can you see me? Ah, there you are! You've unlocked the achievement "Virtio Newbie!"
1 change: 0 additions & 1 deletion disk/lorem.txt

This file was deleted.

Empty file added disk/meow.txt
Empty file.
72 changes: 57 additions & 15 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ qemu_default_bios_path="${QEMU_DEFAULT_BIOS_PATH:-/usr/share/qemu/opensbi-riscv3
qemu_allow_bios_none_fallback="${QEMU_ALLOW_BIOS_NONE_FALLBACK:-0}"
qemu_input="${QEMU_INPUT:-}"
qemu_input_delay_seconds="${QEMU_INPUT_DELAY_SECONDS:-1}"
qemu_disk_source="${QEMU_DISK_SOURCE:-${repo_root}/disk/lorem.txt}"
qemu_disk_dir="${QEMU_DISK_DIR:-${repo_root}/disk}"
qemu_disk_tar="${QEMU_DISK_TAR:-${repo_root}/zig-out/disk.tar}"
qemu_disk_image="${QEMU_DISK_IMAGE:-${repo_root}/zig-out/disk.img}"
qemu_force_timeout="${QEMU_FORCE_TIMEOUT:-0}"

required_tools=(
qemu-system-riscv32
Expand Down Expand Up @@ -54,14 +56,18 @@ if [[ ! -f "${kernel_elf}" ]]; then
exit 0
fi

if [[ ! -f "${qemu_disk_source}" ]]; then
echo "missing disk source: ${qemu_disk_source}" >&2
if [[ ! -d "${qemu_disk_dir}" ]]; then
echo "missing disk dir: ${qemu_disk_dir}" >&2
exit 1
fi

mkdir -p "$(dirname "${qemu_disk_tar}")"
mkdir -p "$(dirname "${qemu_disk_image}")"
cp "${qemu_disk_source}" "${qemu_disk_image}"
truncate -s 1024 "${qemu_disk_image}"
(
cd "${qemu_disk_dir}"
tar cf "${qemu_disk_tar}" --format=ustar -- *.txt
)
cp "${qemu_disk_tar}" "${qemu_disk_image}"

qemu_cmd=(
qemu-system-riscv32
Expand All @@ -80,19 +86,55 @@ qemu_cmd=(
"virtio-blk-device,drive=drive0,bus=virtio-mmio-bus.0"
)

use_timeout=1
display_to_terminal=0
if [[ "${qemu_timeout_seconds}" == "0" ]]; then
use_timeout=0
elif [[ -z "${qemu_input}" && -t 0 && -t 1 && "${qemu_force_timeout}" != "1" ]]; then
use_timeout=0
fi

if [[ -z "${qemu_input}" && -t 0 && -t 1 ]]; then
display_to_terminal=1
fi

set +e
if [[ -n "${qemu_input}" ]]; then
{
sleep "${qemu_input_delay_seconds}"
printf '%b' "${qemu_input}"
} |
timeout --foreground --kill-after=1s "${qemu_timeout_seconds}s" "${qemu_cmd[@]}" \
>"${qemu_log}" 2>&1
status=${PIPESTATUS[1]}
if [[ ${use_timeout} -eq 1 ]]; then
{
sleep "${qemu_input_delay_seconds}"
printf '%b' "${qemu_input}"
} |
timeout --foreground --kill-after=1s "${qemu_timeout_seconds}s" "${qemu_cmd[@]}" \
>"${qemu_log}" 2>&1
status=${PIPESTATUS[1]}
else
{
sleep "${qemu_input_delay_seconds}"
printf '%b' "${qemu_input}"
} |
"${qemu_cmd[@]}" >"${qemu_log}" 2>&1
status=${PIPESTATUS[1]}
fi
else
timeout --foreground --kill-after=1s "${qemu_timeout_seconds}s" "${qemu_cmd[@]}" \
>"${qemu_log}" 2>&1
status=$?
if [[ ${display_to_terminal} -eq 1 ]]; then
if [[ ${use_timeout} -eq 1 ]]; then
timeout --foreground --kill-after=1s "${qemu_timeout_seconds}s" "${qemu_cmd[@]}" 2>&1 | tee "${qemu_log}"
status=${PIPESTATUS[0]}
else
"${qemu_cmd[@]}" 2>&1 | tee "${qemu_log}"
status=${PIPESTATUS[0]}
fi
else
if [[ ${use_timeout} -eq 1 ]]; then
timeout --foreground --kill-after=1s "${qemu_timeout_seconds}s" "${qemu_cmd[@]}" \
>"${qemu_log}" 2>&1
status=$?
else
"${qemu_cmd[@]}" >"${qemu_log}" 2>&1
status=$?
fi
fi
fi
set -e

Expand Down
10 changes: 10 additions & 0 deletions src/common.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub const PAGE_SIZE: usize = 4096;
pub const SYS_PUTCHAR: u32 = 1;
pub const SYS_GETCHAR: u32 = 2;
pub const SYS_EXIT: u32 = 3;
pub const SYS_READFILE: u32 = 4;
pub const SYS_WRITEFILE: u32 = 5;

pub fn alignUp(value: usize, alignment: usize) usize {
return (value + alignment - 1) & ~(alignment - 1);
Expand Down Expand Up @@ -43,6 +45,14 @@ pub fn memcpy(dst: [*]u8, src: [*]const u8, len: size_t) [*]u8 {
return dst;
}

pub fn memEql(lhs: [*]const u8, rhs: []const u8) bool {
var index: usize = 0;
while (index < rhs.len) : (index += 1) {
if (lhs[index] != rhs[index]) return false;
}
return true;
}

pub fn strcpy(dst: [*]u8, src: [*:0]const u8) [*]u8 {
@setRuntimeSafety(false);

Expand Down
167 changes: 167 additions & 0 deletions src/kernel/fs.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const common = @import("common");
const panic_mod = @import("panic.zig");
const sbi = @import("sbi.zig");
const virtio_blk = @import("virtio_blk.zig");

pub const FILES_MAX: usize = 4;
pub const FILE_DATA_MAX: usize = 1024;
pub const DISK_MAX_SIZE: usize = common.alignUp(@sizeOf(File) * FILES_MAX, virtio_blk.SECTOR_SIZE);

pub const TarHeader = extern struct {
name: [100]u8,
mode: [8]u8,
uid: [8]u8,
gid: [8]u8,
size: [12]u8,
mtime: [12]u8,
checksum: [8]u8,
typeflag: u8,
linkname: [100]u8,
magic: [6]u8,
version: [2]u8,
uname: [32]u8,
gname: [32]u8,
devmajor: [8]u8,
devminor: [8]u8,
prefix: [155]u8,
padding: [12]u8,
};

pub const File = extern struct {
in_use: bool,
name: [100]u8,
data: [FILE_DATA_MAX]u8,
size: usize,
};

pub var files: [FILES_MAX]File = [_]File{.{
.in_use = false,
.name = [_]u8{0} ** 100,
.data = [_]u8{0} ** FILE_DATA_MAX,
.size = 0,
}} ** FILES_MAX;
pub var disk: [DISK_MAX_SIZE]u8 = [_]u8{0} ** DISK_MAX_SIZE;

pub fn oct2int(oct: [*]const u8, len: usize) usize {
var dec: usize = 0;
var index: usize = 0;
while (index < len) : (index += 1) {
if (oct[index] < '0' or oct[index] > '7') {
break;
}
dec = dec * 8 + (oct[index] - '0');
}
return dec;
}

pub fn init() void {
var sector: usize = 0;
while (sector < disk.len / virtio_blk.SECTOR_SIZE) : (sector += 1) {
virtio_blk.readWriteDisk(@ptrCast(&disk[sector * virtio_blk.SECTOR_SIZE]), @intCast(sector), false);
}

var off: usize = 0;
var file_index: usize = 0;
while (file_index < FILES_MAX and off + @sizeOf(TarHeader) <= disk.len) : (file_index += 1) {
const header: *TarHeader = @ptrCast(&disk[off]);
if (header.name[0] == 0) {
break;
}
if (!common.memEql(&header.magic, "ustar")) {
panic_mod.PANIC(@src(), "invalid tar header: magic=\"%s\"", .{@as([*:0]const u8, @ptrCast(&header.magic))});
}

const file_size = oct2int(&header.size, header.size.len);
const file = &files[file_index];
file.* = .{
.in_use = true,
.name = [_]u8{0} ** 100,
.data = [_]u8{0} ** FILE_DATA_MAX,
.size = @min(file_size, FILE_DATA_MAX),
};
_ = common.strcpy(&file.name, @ptrCast(&header.name));
const data_offset = off + @sizeOf(TarHeader);
_ = common.memcpy(&file.data, @ptrCast(&disk[data_offset]), file.size);
common.printf(sbi.consolePutchar, "file: %s, size=%d\n", .{ @as([*:0]const u8, @ptrCast(&file.name)), @as(u32, @intCast(file.size)) });

off += common.alignUp(@sizeOf(TarHeader) + file_size, virtio_blk.SECTOR_SIZE);
}
}

fn writeOctalField(field: []u8, value: usize) void {
_ = common.memset(field.ptr, '0', field.len);
if (field.len == 0) return;

var index = field.len - 1;
field[index] = 0;
if (index == 0) return;

var remaining = value;
while (index > 0) {
index -= 1;
field[index] = @intCast((remaining % 8) + '0');
remaining /= 8;
if (remaining == 0) {
while (index > 0) {
index -= 1;
field[index] = '0';
}
break;
}
}
}

pub fn flush() void {
_ = common.memset(&disk, 0, disk.len);

var off: usize = 0;
var file_index: usize = 0;
while (file_index < FILES_MAX) : (file_index += 1) {
const file = &files[file_index];
if (!file.in_use) continue;

const header: *TarHeader = @ptrCast(&disk[off]);
_ = common.memset(@ptrCast(header), 0, @sizeOf(TarHeader));
_ = common.strcpy(&header.name, @ptrCast(&file.name));
writeOctalField(&header.mode, 0o644);
writeOctalField(&header.uid, 0);
writeOctalField(&header.gid, 0);
writeOctalField(&header.size, file.size);
writeOctalField(&header.mtime, 0);
_ = common.strcpy(&header.magic, "ustar");
_ = common.strcpy(&header.version, "00");
header.typeflag = '0';

_ = common.memset(&header.checksum, ' ', header.checksum.len);
var checksum: usize = 0;
const header_bytes: [*]u8 = @ptrCast(header);
var header_index: usize = 0;
while (header_index < @sizeOf(TarHeader)) : (header_index += 1) {
checksum += header_bytes[header_index];
}
_ = common.memset(&header.checksum, 0, header.checksum.len);
writeOctalField(header.checksum[0..6], checksum);
header.checksum[6] = 0;
header.checksum[7] = ' ';

_ = common.memcpy(@ptrCast(&disk[off + @sizeOf(TarHeader)]), &file.data, file.size);
off += common.alignUp(@sizeOf(TarHeader) + file.size, virtio_blk.SECTOR_SIZE);
}

var sector: usize = 0;
while (sector < disk.len / virtio_blk.SECTOR_SIZE) : (sector += 1) {
virtio_blk.readWriteDisk(@ptrCast(&disk[sector * virtio_blk.SECTOR_SIZE]), @intCast(sector), true);
}
common.printf(sbi.consolePutchar, "wrote %d bytes to disk\n", .{@as(u32, @intCast(disk.len))});
}

pub fn lookup(filename: [*:0]const u8) ?*File {
var index: usize = 0;
while (index < FILES_MAX) : (index += 1) {
const file = &files[index];
if (file.in_use and common.strcmp(@ptrCast(&file.name), filename) == 0) {
return file;
}
}
return null;
}
9 changes: 2 additions & 7 deletions src/kernel/main.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const boot = @import("boot.zig");
const common = @import("common");
const fs = @import("fs.zig");
const process = @import("process.zig");
const panic_mod = @import("panic.zig");
const riscv = @import("riscv.zig");
Expand All @@ -23,13 +24,7 @@ pub export fn kernel_main() noreturn {
riscv.writeStvec(@intFromPtr(&trap.kernel_entry));

virtio_blk.init();
var read_buf = [_]u8{0} ** virtio_blk.SECTOR_SIZE;
virtio_blk.readWriteDisk(&read_buf, 0, false);
common.printf(sbi.consolePutchar, "first sector: %s\n", .{@as([*:0]const u8, @ptrCast(&read_buf))});

var write_buf = [_]u8{0} ** virtio_blk.SECTOR_SIZE;
_ = common.strcpy(&write_buf, "hello from kernel!!!\n");
virtio_blk.readWriteDisk(&write_buf, 0, true);
fs.init();

process.idle_proc = process.createProcess(null);
process.idle_proc.pid = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/kernel/process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ fn userEntry() callconv(.naked) noreturn {
\\ sret
:
: [sepc] "r" (paging.USER_BASE),
[sstatus] "r" (riscv.SSTATUS_SPIE),
[sstatus] "r" (riscv.SSTATUS_SPIE | riscv.SSTATUS_SUM),
);
}

Expand Down
21 changes: 21 additions & 0 deletions src/kernel/trap.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const common = @import("common");
const fs = @import("fs.zig");
const panic_mod = @import("panic.zig");
const process = @import("process.zig");
const riscv = @import("riscv.zig");
Expand Down Expand Up @@ -155,6 +156,26 @@ fn handleSyscall(frame: *TrapFrame) void {
process.yield();
panic_mod.PANIC(@src(), "unreachable", .{});
},
common.SYS_READFILE, common.SYS_WRITEFILE => {
const filename: [*:0]const u8 = @ptrFromInt(frame.a0);
const buf: [*]u8 = @ptrFromInt(frame.a1);
var len: usize = frame.a2;
const file = fs.lookup(filename) orelse {
common.printf(sbi.consolePutchar, "file not found: %s\n", .{filename});
frame.a0 = @bitCast(@as(i32, -1));
return;
};
len = @min(len, file.size);
if (frame.a3 == common.SYS_WRITEFILE) {
len = @min(len, fs.FILE_DATA_MAX);
_ = common.memcpy(&file.data, buf, len);
file.size = len;
fs.flush();
} else {
_ = common.memcpy(buf, &file.data, len);
}
frame.a0 = @intCast(len);
},
else => panic_mod.PANIC(@src(), "unexpected syscall a3=%x", .{frame.a3}),
}
}
14 changes: 11 additions & 3 deletions src/user/shell.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,19 @@ pub export fn main() callconv(.c) noreturn {
cmdline[index] = ch_u8;
}

if (restart_prompt) {
continue;
}
if (restart_prompt) continue;

if (common.strcmp(@ptrCast(&cmdline), "hello") == 0) {
common.printf(start_mod.putchar, "Hello world from shell!\n", .{});
} else if (common.strcmp(@ptrCast(&cmdline), "readfile") == 0) {
var buf = [_]u8{0} ** 128;
const len = start_mod.readfile("hello.txt", &buf, buf.len - 1);
if (len >= 0) {
buf[@intCast(len)] = 0;
common.printf(start_mod.putchar, "%s\n", .{@as([*:0]const u8, @ptrCast(&buf))});
}
} else if (common.strcmp(@ptrCast(&cmdline), "writefile") == 0) {
_ = start_mod.writefile("hello.txt", "Hello from shell!\n", 18);
} else if (common.strcmp(@ptrCast(&cmdline), "exit") == 0) {
start_mod.exit();
} else {
Expand Down
Loading
Loading