commit c9c05221d9bd2858d70d36106889864155da738e Author: Sam Roxanne Date: Tue Jun 2 01:23:58 2020 -0500 wot diff --git a/.buildactions/00_setup.lua b/.buildactions/00_setup.lua new file mode 100644 index 0000000..09ff1af --- /dev/null +++ b/.buildactions/00_setup.lua @@ -0,0 +1,2 @@ +os.execute("mkdir -p build") +os.execute("rm -rf build/*") \ No newline at end of file diff --git a/.buildactions/01_velx.lua b/.buildactions/01_velx.lua new file mode 100644 index 0000000..30a2eeb --- /dev/null +++ b/.buildactions/01_velx.lua @@ -0,0 +1,211 @@ +-- VELX builder + +--[[---------------------------------------------------------------------------- + LZSS - encoder / decoder + This is free and unencumbered software released into the public domain. + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + For more information, please refer to +--]]---------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +local M = {} +local string, table = string, table + +-------------------------------------------------------------------------------- +local POS_BITS = 12 +local LEN_BITS = 16 - POS_BITS +local POS_SIZE = 1 << POS_BITS +local LEN_SIZE = 1 << LEN_BITS +local LEN_MIN = 3 + +-------------------------------------------------------------------------------- +function M.compress(input) + local offset, output = 1, {} + local window = '' + + local function search() + for i = LEN_SIZE + LEN_MIN - 1, LEN_MIN, -1 do + local str = string.sub(input, offset, offset + i - 1) + local pos = string.find(window, str, 1, true) + if pos then + return pos, str + end + end + end + + while offset <= #input do + local flags, buffer = 0, {} + + for i = 0, 7 do + if offset <= #input then + local pos, str = search() + if pos and #str >= LEN_MIN then + local tmp = ((pos - 1) << LEN_BITS) | (#str - LEN_MIN) + buffer[#buffer + 1] = string.pack('>I2', tmp) + else + flags = flags | (1 << i) + str = string.sub(input, offset, offset) + buffer[#buffer + 1] = str + end + window = string.sub(window .. str, -POS_SIZE) + offset = offset + #str + else + break + end + end + + if #buffer > 0 then + output[#output + 1] = string.char(flags) + output[#output + 1] = table.concat(buffer) + end + end + + return table.concat(output) +end + +-------------------------------------------------------------------------------- +function M.decompress(input) + local offset, output = 1, {} + local window = '' + + while offset <= #input do + local flags = string.byte(input, offset) + offset = offset + 1 + + for i = 1, 8 do + local str = nil + if (flags & 1) ~= 0 then + if offset <= #input then + str = string.sub(input, offset, offset) + offset = offset + 1 + end + else + if offset + 1 <= #input then + local tmp = string.unpack('>I2', input, offset) + offset = offset + 2 + local pos = (tmp >> LEN_BITS) + 1 + local len = (tmp & (LEN_SIZE - 1)) + LEN_MIN + str = string.sub(window, pos, pos + len - 1) + end + end + flags = flags >> 1 + if str then + output[#output + 1] = str + window = string.sub(window .. str, -POS_SIZE) + end + end + end + + return table.concat(output) +end + + +local function struct(tbl) + local pat = tbl.endian or "=" + local args = {} + for i=1, #tbl do + local a, b = pairs(tbl[i]) + local k, v = a(b) + args[i] = k + pat = pat .. v + end + return setmetatable({}, {__call=function(_, arg) + --checkArg(1, arg, "string", "table") + if (type(arg) == "string") then + local sval = {string.unpack(pat, arg)} + local rtn = {} + for i=1, #args do + rtn[args[i]] = sval[i] + end + return rtn, sval[#sval] + elseif (type(arg) == "table") then + local sval = {} + for i=1, #args do + sval[i] = arg[args[i]] + end + return string.pack(pat, unpack(sval)) + end + end, __len=function() + return string.packsize(pat) + end}) +end + + +local velx_spec = struct { + endian = "<", + {magic="c5"}, + {fver="B"}, + {compression="B"}, + {lver="B"}, + {os="B"}, + {arctype="c4"}, + {psize="I3"}, + {lsize="I3"}, + {ssize="I3"}, + {rsize="I4"} +} + +local function velx_multistep(path, arcpath, args, steps) + local shell_args = "" + for k, v in pairs(args) do + shell_args = shell_args .. k .. "=\"".. v .."\" " + end + steps.precomp() + local h = io.popen(shell_args.."luacomp "..path, "r") + local prog = h:read("*a") + h:close() + prog = steps.postcomp(prog) + steps.prearc() + local arc = "" + if arcpath then + h = io.popen("find "..arcpath.." -depth | tsar -o", "r") + arc = h:read("*a") + h:close() + steps.postarc() + end + if (not args.noz) then + steps.prez() + prog = M.compress(prog) + steps.postz() + end + local header = velx_spec({ + magic = "\27VelX", + compression = (args.noz and 0) or 1, + lver = 0x53, + fver = 1, + os = 0x7F, + psize = #prog, + lsize=0, + ssize=0, + rsize=#arc, + arctype=(arcpath and "tsar") or "" + }) + return header .. prog .. arc +end + +local function velx(path, arcpath, args) + return velx_multistep(path, arcpath, args, { + precomp = function()end, + postcomp = function(prog) return prog end, + prearc = function()end, + postarc = function()end, + prez = function()end, + postz = function()end, + }) +end \ No newline at end of file diff --git a/.buildactions/11_kernel.lua b/.buildactions/11_kernel.lua new file mode 100644 index 0000000..a10e27a --- /dev/null +++ b/.buildactions/11_kernel.lua @@ -0,0 +1,50 @@ +local TSUKI_RELEASE = "0.0.1" +local tz = os.date("%z") +tz = tz:sub(1, 3)..":"..tz:sub(4) +local TSUKI_VERSION = "Lua 5.3 "..os.date("%Y-%m-%dT%H:%M:%S")..tz +local TSUKI_TARGET = os.getenv("TSUKI_TARGET") or "OC" + +function actions.kernel(dbg) + os.execute("mkdir -p build/kernel") + print("Compiling kernel...") + local h = io.open("build/kernel/tkrnl.velx", "wb") + h:write(velx_multistep("ksrc/init.lua", ".doc", { + TSUKI_RELEASE = TSUKI_RELEASE, + TSUKI_VERSION = TSUKI_VERSION, + TSUKI_TARGET = TSUKI_TARGET, + PRINT_DMESG_LEVEL = ((dbg and 0) or 1) + }, { + precomp = function() + print("Compiling kernel...") + end, + postcomp = function(krnl) + os.execute("mkdir .doc") + local h = io.open("build/kernel/debug.lua", "w") + h:write(krnl) + h:close() + print("Generating docs and stripping comments...") + h = io.popen("lua utils/gendocs.lua .doc 2>.ktmp", "w") + h:write(krnl) + h:close() + h = io.open(".ktmp", "rb") + local data = h:read("*a") + h:close() + os.execute("rm -rf .ktmp") + return data + end, + prearc = function() + print("Storing docs...") + end, + postarc = function() + os.execute("rm -rf .doc") + end, + prez = function() + print("Compressing kernel...") + end, + postz = function() + print("Writing kernel out...") + end + })) +end + +actions[#actions+1] = "kernel" \ No newline at end of file diff --git a/.buildactions/20_crescent.lua b/.buildactions/20_crescent.lua new file mode 100644 index 0000000..8fd7b14 --- /dev/null +++ b/.buildactions/20_crescent.lua @@ -0,0 +1,7 @@ +function actions.crescent() + os.execute("mkdir -p build/crescent") + os.execute("cd extras; lua ../utils/mkvelx.lua crescent/init.lua ../build/crescent/boot.velx") + os.execute("cp extras/crescent/bootvelxloader.lua build/crescent/init.lua") +end + +actions[#actions+1] = "crescent" \ No newline at end of file diff --git a/.buildactions/30_velxboot.lua b/.buildactions/30_velxboot.lua new file mode 100644 index 0000000..57ada86 --- /dev/null +++ b/.buildactions/30_velxboot.lua @@ -0,0 +1,7 @@ +function actions.velxboot() + os.execute("mkdir -p build/velxboot") + os.execute("cd extras; luacomp velxboot/init.lua -O ../build/velxboot/bios.lua") + os.execute("cd extras; luacomp velxboot/flashvelxboot.lua -O ../build/velxboot/flashvelxboot.lua") +end + +actions[#actions+1] = "velxboot" \ No newline at end of file diff --git a/.buildactions/40_coreutils.lua b/.buildactions/40_coreutils.lua new file mode 100644 index 0000000..b64340a --- /dev/null +++ b/.buildactions/40_coreutils.lua @@ -0,0 +1,37 @@ +do + --local coreutils = {} + --local function add_coreutil(name, outfolder, args) + -- args = args or {} + -- if not os.execute("[[ -d build/coreutils/"..outfolder.." ]]") then + -- os.execute("mkdir -p build/coreutils/"..outfolder) + -- end + -- actions["coreutil_"..name] = function() + -- local data = velx("coreutils/"..name..".lua", nil, args) + -- local h = io.open("build/coreutils/"..outfolder.."/"..name..".velx", "w") + -- h:write(data) + -- h:close() + -- end + -- coreutils[#coreutils+1] = name + --end + + --add_coreutil("init", "sbin") + --add_coreutil("shutdown", "sbin") + --add_coreutil("sh", "sbin") + --add_coreutil("uname", "sbin") + + --function actions.coreutils() + -- for i=1, #coreutils do + -- actions["coreutil_"..coreutils[i]]() + -- end + --end + local version = "0.1" + function actions.coreutils() + print("Building coreutils...") + os.execute("mkdir -p build/coreutils") + local sks = io.open("build/coreutils/sks.velx", "w") + sks:write(velx("coreutils/multicall.lua", false, { + SKS_COREUTILS_VERSION = version + })) + end + actions[#actions+1] = "coreutils" +end \ No newline at end of file diff --git a/.buildactions/fe_debug.lua b/.buildactions/fe_debug.lua new file mode 100644 index 0000000..481c71e --- /dev/null +++ b/.buildactions/fe_debug.lua @@ -0,0 +1,5 @@ +function actions.debug() + for i=1, #actions do + actions[actions[i]](true) + end +end \ No newline at end of file diff --git a/.buildactions/ff_clean.lua b/.buildactions/ff_clean.lua new file mode 100644 index 0000000..6cf7ace --- /dev/null +++ b/.buildactions/ff_clean.lua @@ -0,0 +1,5 @@ +function actions.clean() + print("Cleaning up...") + --os.execute("rm -rf .docs") + --os.execute("rm -rf .ktmp") +end \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6407c51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.doc +.ktmp +/build/** diff --git a/build.lua b/build.lua new file mode 100644 index 0000000..d527266 --- /dev/null +++ b/build.lua @@ -0,0 +1,27 @@ +local actions = {} + +@[[local h = io.popen("ls .buildactions", "r") +for line in h:lines() do]] +--#include @[{".buildactions/"..line}] +@[[end]] + +--[[function actions.debug() + actions.kernel(true) + actions.crescent() + actions.velxboot() + actions.clean() +end]] + +function actions.all() + for i=1, #actions do + actions[actions[i]]() + end +end + +if not arg[1] then + arg[1] = "all" +end + +actions[arg[1]]() + +print("Build complete.") \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..bca1ec4 --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +luacomp build.lua 2>/dev/null | lua - "$@" \ No newline at end of file diff --git a/coreutils/init.lua b/coreutils/init.lua new file mode 100644 index 0000000..58e02c6 --- /dev/null +++ b/coreutils/init.lua @@ -0,0 +1,57 @@ +local kargs = ... +local kio = kernel.kmode_run("return kio") +local tty = kernel.kmode_run("return tty") +local exec = kernel.kmode_run("return exec") +local thd = require("thread") +local fs = require("filesystem") +local security = require("security") + +kernel.dmesg(kernel.loglevel.INFO, "ksysinit started") +local function run_hooks(hook, args) + local hooks = fs.list("/etc/ksysinit/"..hook) + for i=1, #hooks do + local path = fs.resolve("/etc/ksysinit/"..hook.."/"..hooks[i]) + local stat = fs.stat(path) + if (stat.mode == "file" and stat.uid == 0 and stat.gid == 0 and mode & 2 == 0) then + local f, err = loadfile(path) + if not f then + kernel.dmesg(kernel.loglevel.WARNING, "Failed to load startup hook `"..hooks[i].."': "..err) + goto continue + end + xpcall(function() + f(table.unpack(args)) + end, function(err) + kernel.dmesg(kernel.loglevel.ERROR, "Error in startup hook `"..hooks[i].."': "..debug.traceback(err)) + end) + end + ::continue:: + end +end + +kernel.dmesg(kernel.loglevel.INFO, "running startup hooks") +run_hooks("startup", {}) + +kernel.dmesg(kernel.loglevel.INFO, "starting ttys") +for i=1, tonumber(kargs["tty.count"]) or 1 do + local pty = tty.get(i) + exec.loadfile("/sbin/login.velx", true, { + uid = 0, + gid = 0, + tty = pty + }) +end + +-- Now we wait for shutdown or other things +while true do + local siginfo = kernel.sig_pull() + if siginfo.type == "shutdown" then + run_hooks("shutdown", {}) + kernel.kmode_run([[computer.shutdown()]]) + elseif siginfo.type == "reboot" then + run_hooks("shutdown", {}) + kernel.kmode_run([[computer.shutdown(true)]]) + elseif siginfo.type == "ipc" and siginfo.message[1] == "svcrun" and security.hasperm("svc", siginfo.uid, siginfo.gid) then + table.pack(table.unpack(siginfo.message, 4)) + run_hooks("services/"..siginfo.message[2].."/"..siginfo.message[3], table.pack(table.unpack(siginfo.message, 4))) + end +end \ No newline at end of file diff --git a/coreutils/multicall.lua b/coreutils/multicall.lua new file mode 100644 index 0000000..17a1702 --- /dev/null +++ b/coreutils/multicall.lua @@ -0,0 +1,45 @@ +local calls = {} + +local function version_info() + print("SKS Coreutils version $[{SKS_COREUTILS_VERSION}]") +end + +@[[function add_util(name)]] +calls["@[{name}]"] = function(...) +--#include @[{"coreutils/"..name..".lua"}] +end +@[[end]] + +@[[ +add_util("init") +add_util("reboot") +add_util("sh") +add_util("shutdown") +add_util("svc-ctl") +add_util("uname") +]] + +calls.sks = function(prog, ...) + if not prog then + version_info() + print("SKS Coreutils multicall binary.\n") + print("Calls:") + local t = {} + for k, v in pairs(calls) do + t[#t+1] = k + end + print(table.concat(t, " ")) + return 0 + end + return calls[prog](...) +end + +setmetatable(calls, {__index=function(_, i) + return function() + io.stderr:write("call "..i.." not found. run `sks' without args for list of calls.\n") + end +end}) + +local call = os.getenv("_"):match("/(.+)%.[^%.]+") + +return calls[call](...) \ No newline at end of file diff --git a/coreutils/reboot.lua b/coreutils/reboot.lua new file mode 100644 index 0000000..c7fa770 --- /dev/null +++ b/coreutils/reboot.lua @@ -0,0 +1,5 @@ +if (kernel) then + kernel.sig_push { + sigtype = "shutdown" + } +end \ No newline at end of file diff --git a/coreutils/sh.lua b/coreutils/sh.lua new file mode 100644 index 0000000..e69de29 diff --git a/coreutils/shutdown.lua b/coreutils/shutdown.lua new file mode 100644 index 0000000..687b90a --- /dev/null +++ b/coreutils/shutdown.lua @@ -0,0 +1,10 @@ +if (kernel) then + if (os.getenv("_"):match("reboot.velx")) then + kernel.sig_push {sigtype = "reboot"} + else + kernel.sig_push {sigtype = "shutdown"} + end +else + io.stderr:write("must be run as root\n") + os.exit(-1) +end \ No newline at end of file diff --git a/coreutils/svc-ctl.lua b/coreutils/svc-ctl.lua new file mode 100644 index 0000000..8b2bd24 --- /dev/null +++ b/coreutils/svc-ctl.lua @@ -0,0 +1,6 @@ +if (kernel) then +--#include "coreutils/svc-ctl/init.lua" +else + io.stderr:write("must be run as root\n") + os.exit(-1) +end \ No newline at end of file diff --git a/coreutils/svc-ctl/init.lua b/coreutils/svc-ctl/init.lua new file mode 100644 index 0000000..7d92061 --- /dev/null +++ b/coreutils/svc-ctl/init.lua @@ -0,0 +1,26 @@ +local args = {...} +local thread = require("thread") +local ipc = require("ipc") +local fs = require("filesystem") + +if not args[1] then + local svcs = {} + for f in fs.list("/etc/ksysinit/services") do + svcs[#svcs+1] = f + end + print(table.concat(svcs, ", ")) + return 0 +elseif args[1] and not args[2] then + local svcs = {} + for f in fs.list("/etc/ksysinit/services/"..args[1]) do + svcs[#svcs+1] = f + end + print(table.concat(svcs, ", ")) + return 0 +elseif not fs.exists("/etc/ksysinit/services/"..args[1].."/"..args[2]) then + io.stderr:write("hook `"..args[2].."' not found for `"..args[1].."'\n") + return 1 +else + local proc = thread.get("/sbin/init") + ipc.send(proc, "svcrun", args[1], args[2]) +end diff --git a/coreutils/uname.lua b/coreutils/uname.lua new file mode 100644 index 0000000..64392b1 --- /dev/null +++ b/coreutils/uname.lua @@ -0,0 +1,58 @@ +local sharg = require("sh_arg") +local args = sharg({ + name = "uname", + desc = [[Print certain system information. With no OPTION, same as -s]], + options = { + {"kernel-name", "s", "print the kernel name"}, + {"nodename", "n", "print the network node hostname"}, + {"kernel-release", "r", "print the kernel release"}, + {"kernel-version", "v", "print the kernel release"}, + {"machine", "m", "print the machine hardware name"}, + {"operating-system", "o", "print the operating system"}, + {"version", false, "output version information and exit"} + }, +}, ...) + +if (args.version) then + version_info() + return 0 +end + +if (args.a or args.all) then + args.s = true + args.r = true + args.v = true + args.n = true + args.m = true + args.o = true +end + +if table.select("#", ...) == 0 then + args.s = true +end + +if (args.s) then + io.stdout:write(_KINFO.name.." ") +end + +if (args.n) then + io.stdout:write(os.getenv("HOSTNAME").." ") +end + +if (args.r) then + io.stdout:write(_KINFO.release.." ") +end + +if (args.v) then + io.stdout:write(_KINFO.version.." ") +end + +if (args.m) then + io.stdout:write(_KINFO.machine.." ") +end + +if (args.o) then + io.stdout:write("SKS/Tsuki ") +end + +io.stdout:write("\n") \ No newline at end of file diff --git a/docs/foxfs.md b/docs/foxfs.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/kargs.md b/docs/kargs.md new file mode 100644 index 0000000..8926e2a --- /dev/null +++ b/docs/kargs.md @@ -0,0 +1,22 @@ +# Kernel Arguments + + /boot/kernel/tsuki-0.1.velx [arguments ...] + +## --security +This argument changes the security enviroment of the Tsuki kernel. + +### --security=none +This disables most of Tsuki's security modules, leaving the system in a vulnerable state. + +### --security=noverify +This disables executable verification. This is default. + +### --security=full +This enables all security features. + +### --security=permissive +This makes all security errors into logged messages. + +## --root=partspec +This mounts the partition as the root (`/`). An example partspec would be `osdi(f81aa211-3552-4196-91f6-57cc51c3ebfb,2)` + diff --git a/extras/bootloader/init.lua b/extras/bootloader/init.lua new file mode 100644 index 0000000..e9cdc7c --- /dev/null +++ b/extras/bootloader/init.lua @@ -0,0 +1,16 @@ +local eeprom = component.proxy(component.list("eeprom")()) +local function lzss_decompress(a)local b,c,d,e,j,i,h,g=1,'',''while b<=#a do +e=c.byte(a,b)b=b+1 +for k=0,7 do h=c.sub +g=h(a,b,b)if e>>k&1<1 and b<#a then +i=c.unpack('>I2',a,b)j=1+(i>>4)g=h(d,j,j+(i&15)+2)b=b+1 +end +b=b+1 +c=c..g +d=h(d..g,-4^6)end +end +return c end + +local drive, part = eeprom.getData():match("^([%x%-]+);(%d)$") +part = tonumber(part) + diff --git a/extras/crescent/bootvelxloader.lua b/extras/crescent/bootvelxloader.lua new file mode 100644 index 0000000..ca85015 --- /dev/null +++ b/extras/crescent/bootvelxloader.lua @@ -0,0 +1,16 @@ +local fs = component.proxy(computer.getBootAddress()) +local f = fs.open("boot.velx") +fs.seek(f, "set", 14) +local psize = string.unpack(" 0 then + screen = address + end +end +local cls = function()end +if gpu and screen then + component.invoke(gpu, "bind", screen) + w, h = component.invoke(gpu, "getResolution") + component.invoke(gpu, "setResolution", w, h) + component.invoke(gpu, "setBackground", 0x000000) + component.invoke(gpu, "setForeground", 0xFFFFFF) + component.invoke(gpu, "fill", 1, 1, w, h, " ") + cls = function()component.invoke(gpu,"fill", 1, 1, w, h, " ")end +end +local y = 1 +local function status(msg) + if gpu and screen then + component.invoke(gpu, "set", 1, y, msg) + if y == h then + component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) + component.invoke(gpu, "fill", 1, h, w, 1, " ") + else + y = y + 1 + end + end +end + +local loadspin = 1 +local spins = { + "-", "\\", "|", "/" +} + +local function lsc() + loadspin = (loadspin % 4) + 1 + return spins[loadspin] +end + +--#include "crescent/velx.lua" +--#include "crescent/tsar.lua" +--#include "crescent/loader.lua" + +status("crescent loader v"..version) +boot_kernel("--root=$ --security=none"..$(CRESCENT_KARGS)) \ No newline at end of file diff --git a/extras/crescent/loader.lua b/extras/crescent/loader.lua new file mode 100644 index 0000000..e67b204 --- /dev/null +++ b/extras/crescent/loader.lua @@ -0,0 +1,7 @@ +function boot_kernel(kargs) + local knl, env = load_velx(computer.getBootAddress(), "kernel.velx") + local iramfs = load_tsar(computer.getBootAddress(), "initramfs.tsar") + env._ARCHIVE = iramfs + status("kernel.velx "..kargs) + knl(kargs) +end \ No newline at end of file diff --git a/extras/crescent/tsar.lua b/extras/crescent/tsar.lua new file mode 100644 index 0000000..eb1fff4 --- /dev/null +++ b/extras/crescent/tsar.lua @@ -0,0 +1,97 @@ +local magic = 0x5f7d +local magic_rev = 0x7d5f +local header_fmt = "I2I2I2I2I2I6I6" +local en = string.unpack("=I2", string.char(0x7d, 0x5f)) == magic -- true = LE, false = BE +local function get_end(e) + return (e and "<") or ">" +end +local function read_header(dat) + local e = get_end(en) + local m = string.unpack(e.."I2", dat) + if m ~= magic and m ~= magic_rev then return nil, "bad magic" end + if m ~= magic then + e = get_end(not en) + end + local ent = {} + ent.magic, ent.namesize, ent.mode, ent.uid, ent.gid, ent.filesize, ent.mtime = string.unpack(e..header_fmt, dat) + return ent +end + +local arc = {} + +function arc:fetch(path) + for i=1, #self.tbl do + if (self.tbl[i].name == path and self.tbl[i].mode & 32768 > 0) then + self.seek(self.tbl[i].pos-self.seek(0)) + return self.read(self.tbl[i].filesize), self.tbl[i] + end + end + return nil, "file not found" +end + +function arc:exists(path) + for i=1, #self.tbl do + if (self.tbl[i].name == path) then + return true + end + end + return false +end + +function arc:list(path) + if path:sub(#path) ~= "/" then path = path .. "/" end + local ent = {} + for i=1, #self.tbl do + if (self.tbl[i].name:sub(1, #path) == path and not self.tbl[i].name:find("/", #path+1, false)) then + ent[#ent+1] = self.tbl[i].name + end + end + return ent +end + +function arc:close() + self.close() +end + +function load_tsar(addr, path) + local fs = component.proxy(addr) + local h = fs.open(path) + status("loading initramfs... "..lsc()) + local dat = "" + local buf = "" + repeat + buf = fs.read(h, math.huge) + if (buf) then + dat = dat .. buf + end + y = y - 1 + status("loading initramfs... "..lsc()) + until not buf or buf == "" + local idx = 1 + local function read(a) + local dat = buf:sub(idx, idx+a-1) + idx = idx + a + return dat + end + local function seek(a) + idx = idx + a + return idx + end + local function close()end + local tbl = {} + local lname = "" + while lname ~= "TRAILER!!!" do + y = y - 1 + status("loading initramfs... "..lsc()) + local dat = read(22) + local et = read_header(dat) + et.name = read(et.namesize) + et.pos = seek(et.namesize & 1) + seek(et.filesize + (et.filesize & 1)) + lname = et.name + if lname ~= "TRAILER!!!" then + tbl[#tbl+1] = et + end + end + return setmetatable({tbl = tbl, read = read, seek = seek, close = close}, {__index=arc}) +end diff --git a/extras/crescent/velx.lua b/extras/crescent/velx.lua new file mode 100644 index 0000000..9c6945a --- /dev/null +++ b/extras/crescent/velx.lua @@ -0,0 +1,42 @@ +local function lzss_decompress(a)local b,c,d,e,j,i,h,g=1,'',''while b<=#a do +e=c.byte(a,b)b=b+1 +for k=0,7 do h=c.sub +g=h(a,b,b)if e>>k&1<1 and b<#a then +i=c.unpack('>I2',a,b)j=1+(i>>4)g=h(d,j,j+(i&15)+2)b=b+1 +end +b=b+1 +c=c..g +d=h(d..g,-4^6)end +end +return c end + +local function load_velx(addr, path) + status("loading kernel... "..lsc()) + local fs = component.proxy(addr) + local h = fs.open(path) + local magic, fver, comp, lver, osid, psize = string.unpack(">k&1<1 and b<#a then +i=c.unpack('>I2',a,b)j=1+(i>>4)g=h(d,j,j+(i&15)+2)b=b+1 +end +b=b+1 +c=c..g +d=h(d..g,-4^6)end +end +return c end +local function boot_velx(addr, path) + local fs = component.proxy(addr) + local h = fs.open(path) + local magic, fver, comp, lver, osid, psize = string.unpack(" 0) then + self.seek(self.tbl[i].pos-self.seek(0)) + return self.read(self.tbl[i].filesize), self.tbl[i] + end + end + return nil, "file not found" +end + +function arc:exists(path) + for i=1, #self.tbl do + if (self.tbl[i].name == path) then + return true + end + end + return false +end + +function arc:list(path) + if path:sub(#path) ~= "/" then path = path .. "/" end + local ent = {} + for i=1, #self.tbl do + if (self.tbl[i].name:sub(1, #path) == path and not self.tbl[i].name:find("/", #path+1, false)) then + ent[#ent+1] = self.tbl[i].name + end + end + return ent +end + +function arc:meta(path) + for i=1, #self.tbl do + if (self.tbl[i].name == path) then + return self.tbl[i] + end + end + return nil, "file not found" +end + +function arc:stream(path) + for i=1, #self.tbl do + if (self.tbl[i].name == path) then + return kio.create_stream({dev=self.dev, start=self.tbl[i].pos, size=self.tbl[i].filesize, pos=1}, { + read = function(self, amt) + self.dev:seek("set", self.pos+self.start-1) + if (self.pos+amt-1 > self.size) then + amt = self.size-self.pos + end + local dat = self.dev:read(amt) + self.pos = self.pos + #dat + return dat + end, + seek = function(self, whence, amt) + self.pos = kio.calc_seek(whence, amt, self.size, self.pos) + return self.pos + end, + write = function(self) + return kio.get_error(kio.errno.UNSUPPORTED_OPERATION) + end, + close = function(self) + + end + }) + end + end +end + +function arc:trailer() + self.dev:seek("set", self.tbl.trailer.pos) + return self.dev:read(self.tbl.trailer.filesize) +end + +function arc:close() + self.close() +end + +return { + read = function(dev) + local function read(a) + return dev:read(a) + end + local function seek(a) + return dev:seek("cur", a) + end + local function close() + return dev:close() + end + local start = seek(0) + local tbl = {} + local lname = "" + while lname ~= "TRAILER!!!" do + local dat = read(22) + local et = read_header(dat) + et.name = read(et.namesize) + et.pos = seek(et.namesize & 1) + seek(et.filesize + (et.filesize & 1)) + lname = et.name + if lname ~= "TRAILER!!!" then + tbl[#tbl+1] = et + else + tbl.trailer = et + end + end + return setmetatable({tbl = tbl, read = read, seek = seek, close = close, dev=dev, start=true}, {__index=arc}) + end +} \ No newline at end of file diff --git a/ksrc/bios/unknown/detect.lua b/ksrc/bios/unknown/detect.lua new file mode 100644 index 0000000..2a92563 --- /dev/null +++ b/ksrc/bios/unknown/detect.lua @@ -0,0 +1 @@ +return true \ No newline at end of file diff --git a/ksrc/bios/unknown/docs.lua b/ksrc/bios/unknown/docs.lua new file mode 100644 index 0000000..2bd31d4 --- /dev/null +++ b/ksrc/bios/unknown/docs.lua @@ -0,0 +1,2 @@ +---@page unknown "Unknown" +---@doc "This is for if Tsuki doesn't know what BIOS is in use." diff --git a/ksrc/bios/unknown/info.lua b/ksrc/bios/unknown/info.lua new file mode 100644 index 0000000..4437c50 --- /dev/null +++ b/ksrc/bios/unknown/info.lua @@ -0,0 +1,4 @@ +return { + name = "Unknown", + version = {0, 0, 0, string="0.0.0"} +} \ No newline at end of file diff --git a/ksrc/bios/unknown/quirks.lua b/ksrc/bios/unknown/quirks.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/bios/zoryalegacy/detect.lua b/ksrc/bios/zoryalegacy/detect.lua new file mode 100644 index 0000000..eaefdc4 --- /dev/null +++ b/ksrc/bios/zoryalegacy/detect.lua @@ -0,0 +1 @@ +return _LOAD == "Zorya" \ No newline at end of file diff --git a/ksrc/bios/zoryalegacy/docs.lua b/ksrc/bios/zoryalegacy/docs.lua new file mode 100644 index 0000000..b4a4fc9 --- /dev/null +++ b/ksrc/bios/zoryalegacy/docs.lua @@ -0,0 +1,6 @@ +---@page zorya_legacy "Zorya 1.x (Zorya Legacy)" +---@doc "Zorya 1.x puts a few things in the global space that need to be removed. These include:" +---@doc "- The OEFI library" +---@doc "- The Zorya library" +---@doc "" +---@doc "Note: This *should* also apply to any forks of Zorya Legacy." \ No newline at end of file diff --git a/ksrc/bios/zoryalegacy/info.lua b/ksrc/bios/zoryalegacy/info.lua new file mode 100644 index 0000000..215730c --- /dev/null +++ b/ksrc/bios/zoryalegacy/info.lua @@ -0,0 +1,5 @@ +return { + name = "Zorya", + version = {_ZVER//1, tonumber(tostring(_ZVER):match("%.(%d+)")), _ZPAT, string = _ZVER..".".._ZPAT}, + oefiver = oefi.getAPIVersion() +} \ No newline at end of file diff --git a/ksrc/bios/zoryalegacy/quirks.lua b/ksrc/bios/zoryalegacy/quirks.lua new file mode 100644 index 0000000..16593e3 --- /dev/null +++ b/ksrc/bios/zoryalegacy/quirks.lua @@ -0,0 +1 @@ +zorya = nil \ No newline at end of file diff --git a/ksrc/bios/zoryaneo/detect.lua b/ksrc/bios/zoryaneo/detect.lua new file mode 100644 index 0000000..7ef66ad --- /dev/null +++ b/ksrc/bios/zoryaneo/detect.lua @@ -0,0 +1 @@ +return _BIOS == "Zorya NEO" \ No newline at end of file diff --git a/ksrc/bios/zoryaneo/docs.lua b/ksrc/bios/zoryaneo/docs.lua new file mode 100644 index 0000000..2a310d5 --- /dev/null +++ b/ksrc/bios/zoryaneo/docs.lua @@ -0,0 +1,2 @@ +---@page zorya_neo "Zorya NEO (Zorya 2.0)" +---@doc "There's not much to be done for Zorya NEO as the included Zorya module starts Tsuki with a nice enviroment." \ No newline at end of file diff --git a/ksrc/bios/zoryaneo/info.lua b/ksrc/bios/zoryaneo/info.lua new file mode 100644 index 0000000..631d3ab --- /dev/null +++ b/ksrc/bios/zoryaneo/info.lua @@ -0,0 +1,5 @@ +return { + name = "Zorya NEO", + version = {_ZVER//1, tonumber(tostring(_ZVER):match("%.(%d+)")), _ZPAT, git = _ZGIT, string = _ZVSTR}, + loader = _ZLOADER +} \ No newline at end of file diff --git a/ksrc/bios/zoryaneo/quirks.lua b/ksrc/bios/zoryaneo/quirks.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/biosfixes.lua b/ksrc/biosfixes.lua new file mode 100644 index 0000000..f74c1b5 --- /dev/null +++ b/ksrc/biosfixes.lua @@ -0,0 +1,30 @@ +---@section biosfixes "BIOS fixes" + +@[[do +local i=1]] +local _biossupport = {} +@[[function biosfix(bios)]] +--#include @[{"ksrc/bios/"..bios.."/docs.lua"}] +_biossupport["@[{bios}]"] = { + quirks = function() +--#include @[{"ksrc/bios/"..bios.."/quirks.lua"}] + end, + info = function() +--#include @[{"ksrc/bios/"..bios.."/info.lua"}] + end, + detect = function() +--#include @[{"ksrc/bios/"..bios.."/detect.lua"}] + end, + name = "@[{bios}]", + id = @[{i}] +} +_biossupport[@[{i}]] = "@[{bios}]" +@[[i=i+1]] +@[[end]] + +@[[biosfix("zoryalegacy")]] +@[[biosfix("zoryaneo")]] + +@[[biosfix("unknown")]] +@[[biosfix = nil]] +@[[end]] \ No newline at end of file diff --git a/ksrc/blk/eeprom.lua b/ksrc/blk/eeprom.lua new file mode 100644 index 0000000..fe6f1b7 --- /dev/null +++ b/ksrc/blk/eeprom.lua @@ -0,0 +1,5 @@ +local eeprom = {} + +function eeprom.proxy(addr) + +end \ No newline at end of file diff --git a/ksrc/blk/hdd.lua b/ksrc/blk/hdd.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/blk/partition.lua b/ksrc/blk/partition.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/blk/promcard.lua b/ksrc/blk/promcard.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/blkdev.lua b/ksrc/blkdev.lua new file mode 100644 index 0000000..e35cdab --- /dev/null +++ b/ksrc/blkdev.lua @@ -0,0 +1,4 @@ +--#include "ksrc/blk/eeprom.lua" +--#include "ksrc/blk/hdd.lua" +--#include "ksrc/blk/partition.lua" +--#include "ksrc/blk/promcard.lua" diff --git a/ksrc/buffer.lua b/ksrc/buffer.lua new file mode 100644 index 0000000..625304a --- /dev/null +++ b/ksrc/buffer.lua @@ -0,0 +1,5 @@ +local buffer = {} + +local function create_buffer() + +end \ No newline at end of file diff --git a/ksrc/exec.lua b/ksrc/exec.lua new file mode 100644 index 0000000..20db066 --- /dev/null +++ b/ksrc/exec.lua @@ -0,0 +1,34 @@ + +local velx = (function() +--#include "ksrc/execs/velx_spec/init.lua" +end)() + +local zlua = (function() +--#include "ksrc/execs/zlua/init.lua" +end) + +local lua = (function() +--#include "ksrc/execs/lua/init.lua" +end) + +-- Executable loading process: +-- - Link +-- - Load +-- - Execute + +function exec.loadfile(path, env, runinfo) + local stat = vfs.stat(path) + if (stat & 0x400 > 0) then + runinfo.gid = stat.gid + end + if (stat & 0x800 > 0) then + runinfo.uid = stat.uid + end + if (velx.identify(path)) then + + elseif (zlua.identify(path)) then + + else -- Load Lua...if we can. + + end +end \ No newline at end of file diff --git a/ksrc/execs/lua/init.lua b/ksrc/execs/lua/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/execs/velx_spec/init.lua b/ksrc/execs/velx_spec/init.lua new file mode 100644 index 0000000..fd3f5cb --- /dev/null +++ b/ksrc/execs/velx_spec/init.lua @@ -0,0 +1 @@ +--#include "ksrc/execs/velx_spec/velxspec.lua" diff --git a/ksrc/execs/velx_spec/velxspec.lua b/ksrc/execs/velx_spec/velxspec.lua new file mode 100644 index 0000000..fe17526 --- /dev/null +++ b/ksrc/execs/velx_spec/velxspec.lua @@ -0,0 +1,86 @@ +local velx_spec = struct { + endian = "<", + {magic="c5"}, + {fver="B"}, + {compression="B"}, + {lver="B"}, + {os="B"}, + {arctype="c4"}, + {psize="I3"}, + {lsize="I3"}, + {ssize="I3"}, + {rsize="I4"} +} + +local velx = {} + +local exec = {} + +function velx.identify(path) + local h = io.open(path, "rb") + local header = velx_spec(h:read(#velx_spec)) + h:close() + return header.magic == "\27VelX" +end + +function velx.load(path) + local h = io.open(path, "rb") + local header = velx_spec(h:read(#velx_spec)) + local code = h:read(header.psize) + h:seek("cur", lsize) + local sig = h:read(header.ssize) +end + +function velx.verify(path) + local h = io.open(path, "rb") + local header = velx_spec(h:read(#velx_spec)) + local code = h:read(header.psize) + h:seek("cur", lsize) + local sig = h:read(header.ssize) + h:close() + return security.verify(code, sig) +end + +function exec:link() + self.file:seek("set", #velx_spec+self.header.psize) + local linkinfo = self.file:read(self.header.lsize) + local linker = {} + local pos = 1 + while true do + local size = string.unpack(" size then + pos = size + elseif pos < 1 then + pos = 1 + end + return pos +end + +---@func filestream +---@arg path string "Path to the file" +---@arg mode string "File mode" +---@return stream "The stream for the file." +---@desc "This creates a stream from a file." +function kio.filestream(path, mode) + local dev, rpath = vfs.resolve(path) + local h = dev:open(rpath, mode) + local stat = dev:stat(rpath) + return kio.create_stream({dev=dev, hand=h, stat=stat}, { + read = function(self, amt) + return self.dev:read(self.hand, amt) + end, + seek = function(self, whence, amt) + local pos = self.dev:seek(self.hand, 0) + local npos = kio.calc_seek(whence, amt, self.stat.size, pos) + return self.dev:seek(self.hand, npos-pos) + end, + write = function(self, data) + return self.dev:write(self.hand, data) + end, + close = function(self) + return self.dev:close(self.hand) + end + }) +end + +---@func dir +---@arg udat table "The data to pass to to prototype" +---@arg proto function "The prototype function for the iterator" +---@return function "The iterator." +---@desc "Creates a directory iterator." +function kio.dir(udat, proto) + return function() + return proto(udat) + end +end + +---@func memstream +---@arg str string "The string to create a stream of." +---@arg write boolean "If the stream should be writable or not." +---@return stream "The memory stream." +---@desc "Creates a memory stream." +---@note "stream:close() returns the string." +function kio.memstream(str, write) + return kio.create_stream({str=str, pos=1, write=write}, { + read = function(self, amt) + local dat = self.str:sub(self.pos, self.pos+amt-1) + self.pos = self.pos+#dat + return dat + end, + write = function(self, dat) + if (write) then + local startstr = self.str:sub(1, self.pos-1) + local endstr = self.str:sub(self.pos+#dat) + self.str = startstr..dat..endstr + self.pos = self.pos + #dat + end + end, + seek = function(self, whence, amt) + self.pos = kio.calc_seek(whence, amt, #self.str, self.pos) + return self.pos + end, + close = function(self) + return self.str + end + }) +end + +kio.loglevel = { + DEBUG = 0, + INFO = 1, + WARNING = 2, + ERROR = 3, + PANIC = 255 +} + +kio.levels = { + [0] = "DEBUG", + "INFO", + "WARNING", + "ERROR", + [255] = "PANIC" +} + +---@func dprint +---@arg level integer "The log level" +---@arg status string "The message." +---@desc "This method logs to the kernel log and wherever else is set up to be printed to." +function kio.dprint(level, status) + local stack = {} + local lkexec = {} + local i = 0 + while lkexec do + lkexec = debug.getinfo(i) + stack[#stack+1] = lkexec + end + local src = stack[#stack].source:sub(2) + local spart = vfs.parts(src) + local exec = spart[#spart]:match("^(.+)%.([^%.]+)$") + local message = string.format("[%.2f] [%s] [%s] %s", computer.uptime(), levels[level], exec, status) + dmesgs[#dmesgs+1] = {ut=computer.uptime,lvl=level,x=exec,st=status} + if (level < $[{PRINT_DMESG_LEVEL}]) then + sig.push { + type = "dprint", + msg = message + } + end +end + +---@func dmesg +---@see kio.dprint +kio.dmesg = dprint + +---@func panic +---@arg msg string "The error message to display." +function kio.panic(msg) + dmesg(255, "Kernel panic!") +end + +---@func create_pipe +---@return stream "Pipe in" +---@return stream "Pipe out" +---@desc "Creates a pipe." +function kio.create_pipe() + local pipecore = kio.memstream() + local pipein = kio.create_stream({ + dat = pipecore + }, { + read = function() + -- no + end, + seek = function() + -- no + end, + write = function(self, dat) + self.dat:seek("end", 0) + self.dat:write(dat) + end, + close = function() + -- *no* + end + }) + local pipeout = kio.create_stream({ + dat = pipecore, + pos = 1 + }, { + read = function(self, amt) + self.dat:seek("set", self.pos) + local dat = self.dat:read() + self.pos = self.pos+#dat + return dat + end, + seek = function()end, + write = function()end, + close = function()end + }) + return pipein, pipeout +end + +---@func init +---@desc "Only called once. Sets up the kio library." +function kio.init() +---@func create_buffer +---@arg blocking boolean "True if read calls to a buffer that doesn't contain enough data block." +---@arg pid integer "This is set to the PID of the process which handles the buffers." +---@return table "The buffer for use anywhere." + kio.create_buffer = create_buffer + kio.init = false +end \ No newline at end of file diff --git a/ksrc/kstrings.lua b/ksrc/kstrings.lua new file mode 100644 index 0000000..91dea55 --- /dev/null +++ b/ksrc/kstrings.lua @@ -0,0 +1,6 @@ +_KINFO = { + name = "Tsuki", + release = "$[{TSUKI_RELEASE}]", + version = "$[{TSUKI_VERSION}]", + machine = "$[{TSUKI_TARGET}]", -- OC or LuPI2 +} \ No newline at end of file diff --git a/ksrc/lupi2.lua b/ksrc/lupi2.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/net.lua b/ksrc/net.lua new file mode 100644 index 0000000..6013e71 --- /dev/null +++ b/ksrc/net.lua @@ -0,0 +1,6 @@ +--#include "ksrc/net/tsukinet/init.lua" + +local net = {} +function net.open(netinfo) + +end \ No newline at end of file diff --git a/ksrc/net/tsukinet/init.lua b/ksrc/net/tsukinet/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/security.lua b/ksrc/security.lua new file mode 100644 index 0000000..838d91c --- /dev/null +++ b/ksrc/security.lua @@ -0,0 +1,20 @@ +---@module security Security +local security = {} + +---@func checkacl +---@arg permission string "The permission to check." +---@return boolean "True if the current process has this permission." +function security.checkacl(permission) + local perms = acl.get("group", thread.info().egid, {}) +end + +---@func getmode +---@return string "The current security mode." +function security.getmode() + +end + +function security.init() + klog("security", 1, "Security init started.") + +end diff --git a/ksrc/stdlib/err.lua b/ksrc/stdlib/err.lua new file mode 100644 index 0000000..332e808 --- /dev/null +++ b/ksrc/stdlib/err.lua @@ -0,0 +1,6 @@ +_P.pcall = pcall +_P.xpcall = xpcall + +_P.error = {} +_P.error.geterror = kio.geterror +_P.error.errno = kio.errno \ No newline at end of file diff --git a/ksrc/stdlib/init.lua b/ksrc/stdlib/init.lua new file mode 100644 index 0000000..b922e62 --- /dev/null +++ b/ksrc/stdlib/init.lua @@ -0,0 +1,10 @@ +function _P.load(...) + if (security.checkacl("unsigned_code")) then + + end +end + +--#include "ksrc/stdlib/err.lua" +--#include "ksrc/stdlib/string.lua" +--#include "ksrc/stdlib/thread.lua" +--#include "ksrc/stdlib/err.lua" \ No newline at end of file diff --git a/ksrc/stdlib/io.lua b/ksrc/stdlib/io.lua new file mode 100644 index 0000000..e69de29 diff --git a/ksrc/stdlib/string.lua b/ksrc/stdlib/string.lua new file mode 100644 index 0000000..850e02c --- /dev/null +++ b/ksrc/stdlib/string.lua @@ -0,0 +1,11 @@ +_P.string = {} +@[[function copylib(method)]] +_P.string.@[{method}] = string.@[{method}] +@[[end]] + +@[[for k, v in pairs(string) do + copylib(k) +end]] +@[[copylib("trim")]] +@[[copylib("explode")]] +@[[copylib = nil]] \ No newline at end of file diff --git a/ksrc/stdlib/table.lua b/ksrc/stdlib/table.lua new file mode 100644 index 0000000..081bbdd --- /dev/null +++ b/ksrc/stdlib/table.lua @@ -0,0 +1,9 @@ +_P.table = {} +@[[function copylib(method)]] +_P.table.@[{method}] = table.@[{method}] +@[[end]] + +@[[for k, v in pairs(table) do + copylib(k) +end]] +@[[copylib = nil]] \ No newline at end of file diff --git a/ksrc/stdlib/thread.lua b/ksrc/stdlib/thread.lua new file mode 100644 index 0000000..e4bf71d --- /dev/null +++ b/ksrc/stdlib/thread.lua @@ -0,0 +1,9 @@ +_P.threads = {} + +function _P.threads.add(name, func) + +end + +function _P.threads.load(path, args) + +end \ No newline at end of file diff --git a/ksrc/string.lua b/ksrc/string.lua new file mode 100644 index 0000000..d920355 --- /dev/null +++ b/ksrc/string.lua @@ -0,0 +1,25 @@ +string.newline = "\n" + +function string.trim(self) + return self:gsub("^%s+", ""):gsub("%s+$", "") +end + +function string.explode(self, pat) + local t, ll + t={} + ll=0 + if(#p == 1) then + return {p} + end + while true do + l = string.find(self, pat, ll, true) -- find the next d in the string + if l ~= nil then -- if "not not" found then.. + table.insert(t, string.sub(self,ll,l-1)) -- Save it in our array. + ll = l + 1 -- save just after where we found it for searching next time. + else + table.insert(t, string.sub(self,ll)) -- Save what's left in our array. + break -- Break at end, as it should be, according to the lua manual. + end + end + return t +end \ No newline at end of file diff --git a/ksrc/struct.lua b/ksrc/struct.lua new file mode 100644 index 0000000..e126b5e --- /dev/null +++ b/ksrc/struct.lua @@ -0,0 +1,29 @@ +function struct(tbl) + local pat = tbl.endian or "=" + local args = {} + for i=1, do + local a, b = pairs(tbl[i]) + local k, v = a(b) + args[i] = k + pat = pat .. v + end + return setmetatable({}, {__call=function(_, arg) + checkArg(1, arg, "string", "table") + if (type(arg) == "string") then + local sval = {string.unpack(pat, arg)} + local rtn = {} + for i=1, #args do + rtn[args[i]] = sval[i] + end + return rtn, sval[#sval] + elseif (type(arg) == "table") then + local sval = {} + for i=1, #args do + sval[i] = arg[args[i]] + end + return string.pack(pat, unpack(sval)) + end + end, __len=function() + return string.packsize(pat) + end}) +end \ No newline at end of file diff --git a/ksrc/threads.lua b/ksrc/threads.lua new file mode 100644 index 0000000..b423ca0 --- /dev/null +++ b/ksrc/threads.lua @@ -0,0 +1,56 @@ +local thd = {} +local next_pause = 0 +local max_proctime = 5 -- 5s of process time max. +local last_proctime = 0 +local lastsig = {} +local threads = {} +local function run_threads() + local rthd = {} + local mfd = {} + for i=1, #thd do + if (threads[i].dead) then + mfd[#mfd+1] = i + elseif (threads[i].deadline >= computer.uptime()) then + rthd[#rthd+1] = threads[i] + end + end + table.sort(rthd, function(a, b) + if (a.priority == b.priority) then + return a.deadline < b.deadline + end + return a.priority < b.priority + end) + local starttime = computer.uptime() + local mindl = math.huge + for i=1, #rthd do + local ok, dl = coroutine.resume(rthd[i].coro, table.unpack(rthd[i].args or lastsig)) + if (not ok or coroutine.status(rthd[i].coro) == "dead") then + signal.push(thd.get(rthd[i].parent), {"subproc", "dead", rthd[i].pid, (rthd[i].exitcode or (dl and 1) or 0)}) + for j=1, #thd do + if (threads[j] == rthd[i]) then + mfd[#mfd+1] = j + break + end + end + else + mindl = ((mindl > dl) and dl) or mindl + buffers.update(rthd[i]) + if (rthd[i].sigpushed) then + mindl = 0 + rthd[i].sigpushed = false + end + end + if (computer.uptime() >= starttime+max_proctime) then + goto cleanup + end + end + ::cleanup:: + for i=#mfd, 1, -1 do + table.remove(threads, mfd[i]) + end + lastsig = table.pack(computer.pullSignal(mindl)) +end + +function thd.add(tinfo) + +end \ No newline at end of file diff --git a/ksrc/tty.lua b/ksrc/tty.lua new file mode 100644 index 0000000..ba0ce8f --- /dev/null +++ b/ksrc/tty.lua @@ -0,0 +1,15 @@ +local tty = {} +do + local _tty = {} + function _tty:getgpu() + return self.gpu.address + end + + function _tty:getkb() + return self.kb.address + end + + function _tty:getscreen() + return self.screen.address + end +end \ No newline at end of file diff --git a/ksrc/vfs.lua b/ksrc/vfs.lua new file mode 100644 index 0000000..be6989e --- /dev/null +++ b/ksrc/vfs.lua @@ -0,0 +1,13 @@ +local vfs = {} + +function vfs.mount(path, proxy) + +end + +function vfs.resolve(path) + +end + +function vfs.umount(pathorproxy) + +end \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..47c25a0 --- /dev/null +++ b/readme.md @@ -0,0 +1,6 @@ +# The Tsuki kernel + +The Tsuki kernel is a UNIX-like kernel for OpenComputers and LuPI2. + +## Building +Clone the repo and run `./build.sh`. \ No newline at end of file diff --git a/utils/gendocs.lua b/utils/gendocs.lua new file mode 100644 index 0000000..7a6c24c --- /dev/null +++ b/utils/gendocs.lua @@ -0,0 +1,260 @@ +local outdir = arg[1] + +function string.trim(self) + return self:gsub("^%s+", ""):gsub("%s+$", "") +end + +local escapes = { + ["\\"] = "\\", + ["n"] = "\n", + ["t"] = "\t", + ["r"] = "\r" +} +local docs = {} +local cdoc +local cfunc +local function parse_string(str, close, noesc) + local s = "" + local esc = false + repeat + local c = str.read() + if not esc then + if (c ~= "\\" or noesc) then + s = s .. c + else + esc = true + end + else + if (escapes[c]) then + s = s .. escapes[c] + else + return nil, "invalid escape" + end + end + until str.peek(#close) == close or str.peek(#close) == "" + str.seek(#close) + return s +end + +local function parse_dec(str) + local num = "" + repeat + num = num .. str.read() + until not str.peek():match("%d") or str.peek() == "" + return tonumber(num, 10) +end + +local function parse_hex(str) + if not str.peek():match("%x") then + return nil, "malformed hex" + end + local num = "" + repeat + num = num .. str.read() + until not str.peek():match("%x") or str.peek() == "" + return tonumber(num, 16) +end +local ln = 0 +local function parse_line(line) + local args = {} + local idx = 1 + local str = { + peek = function(amt) + amt = amt or 1 + return line:sub(idx, idx+amt-1) + end, + read = function(amt) + amt = amt or 1 + local data = line:sub(idx, idx+amt-1) + idx = idx + amt + return data + end, + seek = function(amt) + amt = amt or 0 + idx = idx + amt + return idx + end + } + local function lassert(a, b) + return assert(a, "line #"..ln..": "..(b or "unknown error")) + end + while str.peek() ~= "" do + if (str.peek() == "\"") then + str.seek(1) + args[#args+1] = lassert(parse_string(str, "\"", false)) + elseif (str.peek(2) == "0x") then + str.seek(2) + args[#args+1] = lassert(parse_hex(str)) + elseif (str.peek(1):match("%d")) then + args[#args+1] = lassert(parse_dec(str)) + elseif (str.peek() == "'") then + str.seek(1) + args[#args+1] = lassert(parse_string(str, "'", false)) + elseif (str.peek() == " ") then + --do nothing + else + args[#args+1] = lassert(parse_string(str, " ", true)) + end + end + return args +end +local function is_comment(line) + if (line:trim():sub(1, 2) == "--") then + return true, line:sub(3) + end +end +for line in io.stdin:lines() do + ln = ln+1 + local com, rline = is_comment(line) + if (com) then + if (rline:match("^%-@module")) then + local args = parse_line(rline:sub(10)) + if (cdoc) then + if (cfunc) then + cdoc.methods[#cdoc.methods+1] = cfunc + cfunc = nil + end + docs[#docs+1] = cdoc + end + cdoc = { + type = "module", + methods = {}, + name = args[2], + mod = args[1] + } + elseif (rline:match("^%-@section")) then + local args = parse_line(rline:sub(11)) + if (cdoc) then + if (cfunc) then + cdoc.methods[#cdoc.methods+1] = cfunc + cfunc = nil + end + docs[#docs+1] = cdoc + end + cdoc = { + type = "generic", + methods = {}, + name = args[2], + mod = args[1] + } + elseif (rline:match("^%-@page")) then + if (cfunc) then + cdoc.methods[#cdoc.methods+1] = cfunc + end + local args = parse_line(rline:sub(8)) + cfunc = { + type = "page", + doc = {}, + name = args[1], + print_name = args[2] + } + elseif (rline:match("^%-@arg") and cfunc.type == "func") then + local args = parse_line(rline:sub(7)) + cfunc.args[#cfunc.args+1] = { + name = args[1], + type = args[2], + doc = args[3] + } + elseif (rline:match("^%-@return") and cfunc.type == "func") then + local args = parse_line(rline:sub(10)) + cfunc.ret[#cfunc.ret+1] = { + type = args[1], + doc = args[2], + } + elseif (rline:match("^%-@doc")) then + local args = parse_line(rline:sub(7)) + cfunc.doc[#cfunc.doc+1] = { + doc = args[1], + } + elseif (rline:match("^%-@see")) then + local args = parse_line(rline:sub(7)) + cfunc.doc[#cfunc.doc+1] = { + doc = "See \27_S"..args[1].."\27_E" + } + elseif (rline:match("^%-@note")) then + local args = parse_line(rline:sub(8)) + cfunc.doc[#cfunc.doc+1] = { + doc = "NOTE: "..args[1] + } + elseif (rline:match("^%-@desc")) then + local args = parse_line(rline:sub(8)) + cfunc.doc[#cfunc.doc+1] = { + doc = args[1] + } + elseif (rline:match("^%-@vararg")) then + cfunc.vararg = true + elseif (rline:match("^%-@varret")) then + cfunc.varret = true + elseif (rline:match("^%-@func")) then + if (cfunc) then + cdoc.methods[#cdoc.methods+1] = cfunc + end + local args = parse_line(rline:sub(8)) + cfunc = { + type = "func", + doc = {}, + name = args[1], + ret = {}, + args = {} + } + end + else + io.stderr:write(line.."\n") + end +end + +if (cfunc) then + cdoc.methods[#cdoc.methods+1] = cfunc +end +if (cdoc) then + docs[#docs+1] = cdoc +end +for i=1, #docs do + os.execute("mkdir -p \""..outdir.."/"..docs[i].mod:trim().."\"") + for j=1, #docs[i].methods do + local docc = "" + local f = io.open(outdir.."/"..docs[i].mod:trim().."/"..docs[i].methods[j].name:trim()..".tdf", "wb") + if (docs[i].methods[j].type == "func") then + local argtext = "" + local fun = docs[i].mod.."."..docs[i].methods[j].name.."(" + if (#docs[i].methods[j].args > 0) then + for k=1, #docs[i].methods[j].args do + fun = fun .. docs[i].methods[j].args[k].name..":"..docs[i].methods[j].args[k].type .. ", " + argtext = argtext .. docs[i].methods[j].args[k].name .. " - " .. docs[i].methods[j].args[k].doc .. "\n" + end + if (docs[i].methods[j].vararg) then + fun = fun .. "..." + else + fun = fun:sub(1, #fun-2) + end + else + argtext = "No arguments" + end + fun = fun .. ")" + + local rettext = "" + if (#docs[i].methods[j].ret > 0) then + fun = fun .. ":" + for k=1, #docs[i].methods[j].ret do + fun = fun .. docs[i].methods[j].ret[k].type .. ", " + rettext = rettext .. docs[i].methods[j].ret[k].type .. " - " .. docs[i].methods[j].ret[k].doc .. "\n" + end + if (docs[i].methods[j].varret) then + fun = fun .. "..." + else + fun = fun:sub(1, #fun-2) + end + else + rettext = "No return." + end + docc = docc .. fun .. "\n\nArguments:\n"..argtext.."\n\nReturns:\n"..rettext.."\n\n" + else + docc = docc .. docs[i].methods[j].print_name .. "\n\n" + end + for k=1, #docs[i].methods[j].doc do + docc = docc..docs[i].methods[j].doc[k].doc .. "\n" + end + f:write(docc) + f:close() + end +end \ No newline at end of file diff --git a/utils/mkvelx.lua b/utils/mkvelx.lua new file mode 100644 index 0000000..05825f0 --- /dev/null +++ b/utils/mkvelx.lua @@ -0,0 +1,64 @@ +local f = io.popen("luacomp "..arg[1], "r") +local prog = f:read("*a") +f:close() + +local function struct(tbl) + local pat = tbl.endian or "=" + local args = {} + for i=1, #tbl do + local a, b = pairs(tbl[i]) + local k, v = a(b) + args[i] = k + pat = pat .. v + end + return setmetatable({}, {__call=function(_, arg) + --checkArg(1, arg, "string", "table") + if (type(arg) == "string") then + local sval = {string.unpack(pat, arg)} + local rtn = {} + for i=1, #args do + rtn[args[i]] = sval[i] + end + return rtn, sval[#sval] + elseif (type(arg) == "table") then + local sval = {} + for i=1, #args do + sval[i] = arg[args[i]] + end + return string.pack(pat, unpack(sval)) + end + end, __len=function() + return string.packsize(pat) + end}) +end + + +local velx_spec = struct { + endian = "<", + {magic="c5"}, + {fver="B"}, + {compression="B"}, + {lver="B"}, + {os="B"}, + {arctype="c4"}, + {psize="I3"}, + {lsize="I3"}, + {ssize="I3"}, + {rsize="I4"} +} + +f = io.open(arg[2], "wb") +f:write(velx_spec({ + magic="\27VelX", + fver=1, + compression=0, + lver=0x53, + os=127, + arctype="", + psize=#prog, + lsize=0, + ssize=0, + rsize=0, +})) +f:write(prog) +f:close() \ No newline at end of file diff --git a/utils/velxdump.lua b/utils/velxdump.lua new file mode 100644 index 0000000..36c994d --- /dev/null +++ b/utils/velxdump.lua @@ -0,0 +1,207 @@ +--[[---------------------------------------------------------------------------- + LZSS - encoder / decoder + This is free and unencumbered software released into the public domain. + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + For more information, please refer to +--]]---------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +local M = {} +local string, table = string, table + +-------------------------------------------------------------------------------- +local POS_BITS = 12 +local LEN_BITS = 16 - POS_BITS +local POS_SIZE = 1 << POS_BITS +local LEN_SIZE = 1 << LEN_BITS +local LEN_MIN = 3 + +-------------------------------------------------------------------------------- +function M.compress(input) + local offset, output = 1, {} + local window = '' + + local function search() + for i = LEN_SIZE + LEN_MIN - 1, LEN_MIN, -1 do + local str = string.sub(input, offset, offset + i - 1) + local pos = string.find(window, str, 1, true) + if pos then + return pos, str + end + end + end + + while offset <= #input do + local flags, buffer = 0, {} + + for i = 0, 7 do + if offset <= #input then + local pos, str = search() + if pos and #str >= LEN_MIN then + local tmp = ((pos - 1) << LEN_BITS) | (#str - LEN_MIN) + buffer[#buffer + 1] = string.pack('>I2', tmp) + else + flags = flags | (1 << i) + str = string.sub(input, offset, offset) + buffer[#buffer + 1] = str + end + window = string.sub(window .. str, -POS_SIZE) + offset = offset + #str + else + break + end + end + + if #buffer > 0 then + output[#output + 1] = string.char(flags) + output[#output + 1] = table.concat(buffer) + end + end + + return table.concat(output) +end + +-------------------------------------------------------------------------------- +function M.decompress(input) + local offset, output = 1, {} + local window = '' + + while offset <= #input do + local flags = string.byte(input, offset) + offset = offset + 1 + + for i = 1, 8 do + local str = nil + if (flags & 1) ~= 0 then + if offset <= #input then + str = string.sub(input, offset, offset) + offset = offset + 1 + end + else + if offset + 1 <= #input then + local tmp = string.unpack('>I2', input, offset) + offset = offset + 2 + local pos = (tmp >> LEN_BITS) + 1 + local len = (tmp & (LEN_SIZE - 1)) + LEN_MIN + str = string.sub(window, pos, pos + len - 1) + end + end + flags = flags >> 1 + if str then + output[#output + 1] = str + window = string.sub(window .. str, -POS_SIZE) + end + end + end + + return table.concat(output) +end + + +local function struct(tbl) + local pat = tbl.endian or "=" + local args = {} + for i=1, #tbl do + local a, b = pairs(tbl[i]) + local k, v = a(b) + args[i] = k + pat = pat .. v + end + return setmetatable({}, {__call=function(_, arg) + --checkArg(1, arg, "string", "table") + if (type(arg) == "string") then + local sval = {string.unpack(pat, arg)} + local rtn = {} + for i=1, #args do + rtn[args[i]] = sval[i] + end + return rtn, sval[#sval] + elseif (type(arg) == "table") then + local sval = {} + for i=1, #args do + sval[i] = arg[args[i]] + end + return string.pack(pat, unpack(sval)) + end + end, __len=function() + return string.packsize(pat) + end}) +end + +local velx_spec = struct { + endian = "<", + {magic="c5"}, + {fver="B"}, + {compression="B"}, + {lver="B"}, + {os="B"}, + {arctype="c4"}, + {psize="I3"}, + {lsize="I3"}, + {ssize="I3"}, + {rsize="I4"} +} + +local compression = { + [0] = "None", + [1] = "LZSS" +} + +local os = { + [0] = "Tsuki", + [0x7F] = "BIOS", +} + +local commands = { + ["cpio"] = "cpio -t", + ["tar"] = "tar -t", + ["tsar"] = "tsar -t", + ["zip"] = "jar -t" +} + +local f = io.open(arg[1], "rb") +local header = velx_spec(f:read(#velx_spec)) +local program = f:read(header.psize) +local linker = f:read(header.lsize) +local sigs = f:read(header.ssize) +local archive = f:read(header.rsize) +local lver = string.format("%x", header.lver) +lver = lver:sub(1,1).."."..lver:sub(2,2) +io.stdout:write(string.format([[File Version: %d +Compression: %s +Type: %s +OS: %s +Lua Version: %s +Archive type: %s]], +header.fver, +compression[header.compression] or "Unknown", +(((header.os & 0x80) > 0) and "Library") or "Executable", +os[header.os & 0x7F] or "Unknown", +lver, +header.arctype:gsub("\0", "")),"\n") +if (header.arctype ~= "\0\0\0\0" and archive ~= "") then + local h = io.popen(commands[header.arctype:gsub("\0", "")], "w") + h:write(archive) + h:close() +end +if (header.compression == 1) then + io.stderr:write(M.decompress(program)) +elseif (header.compression == 0) then + io.stderr:write(program) +end \ No newline at end of file