From 7c1bd03a71cccfcf48e79d241787021b0de98e73 Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 4 Oct 2026 02:02:15 +0200 Subject: feat: add more user programs --- kernel/fs.rv | 83 ++++++++++++++++++++++++++++++++++++++++++--------------- kernel/main.rv | 9 +++++-- kernel/proc.rv | 22 ++++++++++++--- kernel/seed.rv | 5 ++++ kernel/shell.rv | 56 ++++++++++++++++++++++++++++++-------- 5 files changed, 136 insertions(+), 39 deletions(-) create mode 100644 kernel/seed.rv (limited to 'kernel') diff --git a/kernel/fs.rv b/kernel/fs.rv index adb317c..5d72bc1 100644 --- a/kernel/fs.rv +++ b/kernel/fs.rv @@ -5,10 +5,7 @@ # sectors 1..8 directory, 128 entries of 32 bytes (16 per sector) # sectors 9..136 fat, one 2 byte entry per sector on the whole disk # sectors 137.. file data, one block == one sector -# -# a fat entry is the next block in a files chain, 0 means free, END means last -# block of a chain. a file just stores its first block + byte size in its dir -# entry and we walk the chain from there +# a fat entry points at the next block in a files chain, 0 is free, END is the last const SB_LBA = 0 const DIR_LBA = 1 @@ -66,12 +63,7 @@ end fn dir_lba(i) DIR_LBA + (i shr 4) fn dir_off(i) (i band 15) * 32 -# entry byte layout: base is fs.buf + dir_off(i) after loading the sector -# +0 used (0/1) -# +1 name length -# +2 first block (2) -# +4 size (4) -# +8 name bytes (up to NAME_MAX) +# dir entry, 32 bytes: +0 used, +1 name len, +2 first block, +4 size, +8 name bytes fn name_at(base, len) do let out = "" for k in 0..len do out = out ~ string.of_ascii(mem_read(base + 8 + k, 1)) end @@ -108,13 +100,19 @@ fn fs_init() do fs.buf = frame_alloc() fs.fat = frame_alloc() fs.total = disk_sectors() - if disk_present() == 0 do fs.ready = :false return :nil end + if fs.total == 0 do fs.ready = :false return :nil end disk_read(SB_LBA, fs.buf) fs.ready = if mem_read(fs.buf, 4) == MAGIC :true else :false end +fn fs_ready() do + if fs.ready return :true + puts("no filesystem, run mkfs first\n") + :false +end + fn fs_format() do - if disk_present() == 0 do puts("mkfs: no disk\n") return :nil end + if disk_sectors() == 0 do puts("mkfs: no disk\n") return :nil end mem_zero(fs.buf, BLOCK) mem_write(fs.buf, MAGIC, 4) mem_write(fs.buf + 4, fs.total, 4) @@ -126,10 +124,10 @@ fn fs_format() do puts("formatted " ~ fs.total ~ " sectors, " ~ (fs.total shr 1) ~ " KiB\n") end -# write a file, gee i wonder what a function called `fs_write` does -fn fs_write(name, text) do - if fs.ready == :false do puts("no filesystem, run mkfs first\n") return :nil end - if name:len() > NAME_MAX do puts("name too long (max " ~ NAME_MAX ~ ")\n") return :nil end +# silent core of fs_write, global so the bridge can reach it via getGlobal +global fs_store = fn(name, text) do + if fs_ready() == :false return 0 + if name:len() > NAME_MAX do puts("name too long (max " ~ NAME_MAX ~ ")\n") return 0 end let idx = fs_find(name) if idx >= 0 do disk_read(dir_lba(idx), fs.buf) @@ -137,7 +135,7 @@ fn fs_write(name, text) do end else do idx = dir_alloc() - if idx < 0 do puts("directory full\n") return :nil end + if idx < 0 do puts("directory full\n") return 0 end end const size = text:len() @@ -146,7 +144,7 @@ fn fs_write(name, text) do let written = 0 while written < size do const block = fat_alloc() - if block == 0 do puts("disk full\n") return :nil end + if block == 0 do puts("disk full\n") return 0 end fat_set(block, END) # reserve if first == 0 do first = block end else do fat_set(prev, block) end @@ -169,11 +167,52 @@ fn fs_write(name, text) do mem_write(base + 4, size, 4) for k in 0..name:len() do mem_write(base + 8 + k, name[k]:ascii(), 1) end disk_write(dir_lba(idx), fs.buf) - puts("wrote " ~ name ~ " (" ~ size ~ " bytes)\n") + return 1 +end + +# write a file, gee i wonder what a function called `fs_write` does +fn fs_write(name, text) do + if fs_store(name, text) == 1 puts("wrote " ~ name ~ " (" ~ text:len() ~ " bytes)\n") +end + +global fs_size = fn(name) do + const idx = fs_find(name) + if idx < 0 return -1 + disk_read(dir_lba(idx), fs.buf) + return mem_read(fs.buf + dir_off(idx) + 4, 4) +end + +# read up to len bytes from offset, returned as a string (shorter at eof) +global fs_read_bytes = fn(name, offset, len) do + const idx = fs_find(name) + if idx < 0 return "" + disk_read(dir_lba(idx), fs.buf) + const base = fs.buf + dir_off(idx) + let block = mem_read(base + 2, 2) + const size = mem_read(base + 4, 4) + let stop = offset + len + if stop > size do stop = size end + let pos = 0 + let out = "" + while (block != 0 and block != END and pos < stop) do + const nxt = fat_get(block) + if (pos + BLOCK > offset and pos < stop) do + disk_read(block, fs.buf) + let k = 0 + while (k < BLOCK) do + const abs = pos + k + if (abs >= offset and abs < stop) do out = out ~ string.of_ascii(mem_read(fs.buf + k, 1)) end + k += 1 + end + end + pos += BLOCK + block = nxt + end + return out end fn fs_cat(name) do - if fs.ready == :false do puts("no filesystem, run mkfs first\n") return :nil end + if fs_ready() == :false return :nil const idx = fs_find(name) if idx < 0 do puts("cat: " ~ name ~ ": no such file\n") return :nil end disk_read(dir_lba(idx), fs.buf) @@ -194,7 +233,7 @@ fn fs_cat(name) do end fn fs_ls() do - if fs.ready == :false do puts("no filesystem, run mkfs first\n") return :nil end + if fs_ready() == :false return :nil let count = 0 for i in 0..DIR_ENTRIES do disk_read(dir_lba(i), fs.buf) @@ -208,7 +247,7 @@ fn fs_ls() do end fn fs_rm(name) do - if fs.ready == :false do puts("no filesystem, run mkfs first\n") return :nil end + if fs_ready() == :false return :nil const idx = fs_find(name) if idx < 0 do puts("rm: " ~ name ~ ": no such file\n") return :nil end disk_read(dir_lba(idx), fs.buf) diff --git a/kernel/main.rv b/kernel/main.rv index 896bb54..24852fe 100644 --- a/kernel/main.rv +++ b/kernel/main.rv @@ -9,9 +9,14 @@ cmd_clear() dump_memmap() fs_init() -if disk_present() == 0 puts("no disk attached\n") +if disk_sectors() == 0 puts("no disk attached\n") else if fs.ready puts("mounted revelfs on disk\n") -else puts("disk has no filesystem, run `mkfs`\n") +else do + # blank disk, first boot after a clone. format it and drop the default files on + puts("blank disk, formatting + seeding default files\n") + fs_format() + seed_fs() +end puts("revel shell, live in revo. type `help`\n% ") flush_console() diff --git a/kernel/proc.rv b/kernel/proc.rv index e2f451b..3303bc1 100644 --- a/kernel/proc.rv +++ b/kernel/proc.rv @@ -1,12 +1,11 @@ -# parse the embedded elf (physical address from elf_phys()) and run it in ring 3 +# load an embedded elf into a fresh address space and run it in ring 3 const PT_LOAD = 1 const PAGE_FLAGS = 6 # write | user -fn cmd_run() do - const base = elf_phys() +fn load_elf(base) do if mem_read(base, 4) != 0x464c457f do - puts("run: not an ELF\n") + puts("not an ELF\n") return :nil end const entry = mem_read(base + 24, 8) @@ -38,3 +37,18 @@ fn cmd_run() do as_map(space, stack_top - 4096, frame_alloc(), PAGE_FLAGS) proc_run(space, entry, stack_top) end + +# launch a program by name, :true if one matched +fn run_prog(name) do + for i in 0..prog_count() do + if prog_name(i) == name do load_elf(prog_phys(i)) return :true end + end + puts("no such program: " ~ name ~ "\n") + return :false +end + +fn list_progs() do + puts("programs:") + for i in 0..prog_count() do puts(" " ~ prog_name(i)) end + puts("\n") +end diff --git a/kernel/seed.rv b/kernel/seed.rv new file mode 100644 index 0000000..6c0a73f --- /dev/null +++ b/kernel/seed.rv @@ -0,0 +1,5 @@ +# js dw about this ok? +fn seed_fs() do + fs_write("i have big balls.txt", "dude my balls are actually massive") + fs_write("balls.txt", "its lowk insane how big my balls are") +end diff --git a/kernel/shell.rv b/kernel/shell.rv index 6e3c2e4..35eac37 100644 --- a/kernel/shell.rv +++ b/kernel/shell.rv @@ -1,15 +1,42 @@ # this is just a simple prompt to test that shit is working fn cmd_help() do - puts("commands: help, clear, bars, echo , time, mem, uptime, ps, run, zoom in|out\n") + puts("commands: help, clear, bars, echo , time, mem, uptime, ps, run , zoom in|out\n") puts("fs: mkfs, ls, cat , write , rm \n") + puts(" names with spaces go in \"quotes\"\n") end -fn cmd_write(line, words) do - if words:len() < 2 do puts("usage: write \n") return :nil end - const name = words[1] - const start = 6 + name:len() + 1 - const text = if line:len() > start line:sub(start, line:len() - start) else "" - fs_write(name, text) +fn arg_name(line, start) do + let i = start + let name = "" + if (i < line:len() and line[i]:ascii() == 34) do + i += 1 + while (i < line:len() and line[i]:ascii() != 34) do name = name ~ line[i] i += 1 end + if i < line:len() do i += 1 end + end + else do + while (i < line:len() and line[i]:ascii() != 32) do name = name ~ line[i] i += 1 end + end + if (i < line:len() and line[i]:ascii() == 32) do i += 1 end + { name = name, rest = i } +end + +fn cmd_write(line) do + const a = arg_name(line, 6) + if a.name:len() == 0 do puts("usage: write \n") return :nil end + const text = if line:len() > a.rest line:sub(a.rest, line:len() - a.rest) else "" + fs_write(a.name, text) +end + +fn cmd_cat(line) do + const a = arg_name(line, 4) + if a.name:len() == 0 do puts("usage: cat \n") return :nil end + fs_cat(a.name) +end + +fn cmd_rm(line) do + const a = arg_name(line, 3) + if a.name:len() == 0 do puts("usage: rm \n") return :nil end + fs_rm(a.name) end fn zoom_by(delta) do @@ -85,7 +112,14 @@ fn run(line) do if line:trim():len() == 0 do puts("% ") return :nil end const words = line:split(" ") const cmd = words[0] - if cmd == "run" do cmd_run() return :nil end + # run launches a process, its prompt gets reprinted by on_tick when it exits so + # it lands below the programs output. no arg just lists whats available + if cmd == "run" do + if words:len() <= 1 do list_progs() puts("% ") return :nil end + if run_prog(words[1]) == :true return :nil + puts("% ") + return :nil + end match cmd | "help" => cmd_help() | "clear" => cmd_clear() @@ -97,9 +131,9 @@ fn run(line) do | "zoom" => cmd_zoom(words) | "mkfs" => fs_format() | "ls" => fs_ls() - | "cat" => if words:len() > 1 fs_cat(words[1]) else puts("usage: cat \n") - | "write" => cmd_write(line, words) - | "rm" => if words:len() > 1 fs_rm(words[1]) else puts("usage: rm \n") + | "cat" => cmd_cat(line) + | "write" => cmd_write(line) + | "rm" => cmd_rm(line) | "echo" => if line:len() > 5 puts(line:sub(5, line:len() - 5) ~ "\n") else puts("\n") | _ => puts("unknown command: " ~ cmd ~ "\n") puts("% ") -- cgit v1.3