This commit is contained in:
Sam Roxanne 2020-06-02 01:23:58 -05:00
commit c9c05221d9
91 changed files with 2530 additions and 0 deletions

View File

@ -0,0 +1,2 @@
os.execute("mkdir -p build")
os.execute("rm -rf build/*")

211
.buildactions/01_velx.lua Normal file
View File

@ -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 <http://unlicense.org/>
--]]----------------------------------------------------------------------------
--------------------------------------------------------------------------------
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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -0,0 +1,5 @@
function actions.debug()
for i=1, #actions do
actions[actions[i]](true)
end
end

View File

@ -0,0 +1,5 @@
function actions.clean()
print("Cleaning up...")
--os.execute("rm -rf .docs")
--os.execute("rm -rf .ktmp")
end

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/.doc
.ktmp
/build/**

27
build.lua Normal file
View File

@ -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.")

2
build.sh Executable file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env sh
luacomp build.lua 2>/dev/null | lua - "$@"

57
coreutils/init.lua Normal file
View File

@ -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

45
coreutils/multicall.lua Normal file
View File

@ -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](...)

5
coreutils/reboot.lua Normal file
View File

@ -0,0 +1,5 @@
if (kernel) then
kernel.sig_push {
sigtype = "shutdown"
}
end

0
coreutils/sh.lua Normal file
View File

10
coreutils/shutdown.lua Normal file
View File

@ -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

6
coreutils/svc-ctl.lua Normal file
View File

@ -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

View File

@ -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

58
coreutils/uname.lua Normal file
View File

@ -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")

0
docs/foxfs.md Normal file
View File

22
docs/kargs.md Normal file
View File

@ -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)`

View File

@ -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)

View File

@ -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("<I3", fs.read(f, 3))
fs.seek(f, "set", 27)
local rdat = psize
local dat = ""
local buf = ""
repeat
buf = fs.read(f, rdat)
if (buf) then
dat = dat .. buf
end
rdat = psize - #dat
until rdat == 0 or not buf or buf == ""
load(dat, "=boot.velx")()

53
extras/crescent/init.lua Normal file
View File

@ -0,0 +1,53 @@
@[[if not svar.get("CRESCENT_KARGS") then
svar.set("CRESCENT_KARGS", "")
end]]
local version = "1.0"
local gpu = component.list("gpu")()
local w, h
local screen = component.list('screen')()
for address in component.list('screen') do
if #component.invoke(address, 'getKeyboards') > 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))

View File

@ -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

97
extras/crescent/tsar.lua Normal file
View File

@ -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

42
extras/crescent/velx.lua Normal file
View File

@ -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("<c5BBBBxxxxI3xxxxxxxxxx", fs.read(h, 13))
if (magic ~= "\27VelX" or fver ~= 1 or osid ~= 127) then
return
end
local rdat = psize
local dat = ""
local buf = ""
repeat
buf = fs.read(h, rdat)
if (buf) then
dat = dat .. buf
end
rdat = psize - #dat
y = y - 1
status("loading kernel... "..lsc())
until rdat == 0 or not buf or buf == ""
y = y - 1
status("loading kernel... ")
if (comp == 1) then
status("decompressing kernel...")
dat = lzss_decompress(dat)
end
local env = {}
env._G = env
return load(dat, "="..path, "t", setmetatable(env, {__index=_G})), env
end

View File

@ -0,0 +1,72 @@
local component = require("component")
local computer = require("computer")
print("Flashing velxboot...")
component.eeprom.set([[
--#include "velxboot/init.lua"
]])
component.eeprom.setData(computer.getBootAddress()..";boot.velx")
print("Creating boot.velx...")
local f = io.open("/init.lua")
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("/boot.velx", "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()

67
extras/velxboot/init.lua Normal file
View File

@ -0,0 +1,67 @@
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 function boot_velx(addr, path)
local fs = component.proxy(addr)
local h = fs.open(path)
local magic, fver, comp, lver, osid, psize = string.unpack("<c5BBBBxxxxI3xxxxxxxxxx", fs.read(h, 13))
if (magic ~= "\27VelX" or fver ~= 1 or osid ~= 127) then
return
end
local rdat = psize
local dat = ""
local buf = ""
repeat
buf = fs.read(h, rdat)
if (buf) then
dat = dat .. buf
end
rdat = psize - #dat
until rdat == 0 or not buf or buf == ""
if (comp == 1) then
dat = lzss_decompress(dat)
end
local baddr = addr
local bpath = path
function computer.getBootAddress()
return baddr
end
function computer.getBootPath()
return bpath
end
function computer.setBootAddress(a)
eeprom.setData(a..";"..path)
addr = a
end
function computer.setBootPath(p)
eeprom.setData(addr..";"..p)
path = p
end
assert(load(dat, "="..path))()
end
local config = eeprom.getData()
local addr, path = config:match("^(.+);(.+)$")
if not addr then
addr = config
path = "boot.velx"
else
return boot_velx(addr, path)
end
if not component.invoke(addr, "exists", path) then
for d in component.list("filesystem") do
if (component.invoke(d, "exists", "boot.velx")) then
eeprom.setData(d..";boot.velx")
boot_velx(d, "boot.velx")
end
end
end

21
ksrc/acl.lua Normal file
View File

@ -0,0 +1,21 @@
local acl = {}
do
local acl = struct({
meta = "B",
id = "H"
})
local function acl_readstream(stream)
local a = acl(stream:read(#acl))
local perm = stream:read(a.meta & 0x3f)
local
end
function acl.read(stream, dperm, gid, uid)
end
function _acl:get_effective_perm(id, group, perm)
end
end

9
ksrc/ads.lua Normal file
View File

@ -0,0 +1,9 @@
local ads = {}
function ads.get(path)
end
function ads.list(path)
end

10
ksrc/archives.lua Normal file
View File

@ -0,0 +1,10 @@
local archives = {}
archives.tsar = (function()
--#include "ksrc/arcs/tsar/init.lua"
end)()
function archive.parse(stream, atype)
if archives[atype] then
return archives[atype].read(stream)
end
end

View File

124
ksrc/arcs/tsar/init.lua Normal file
View File

@ -0,0 +1,124 @@
local magic = 0x5f7d
local magic_rev = 0x7d5f
local header = "I2I2I2I2I2I6I6"
local function read_header(dat)
local e = "<"
local m = string.unpack("<I2", dat)
if m ~= magic and m ~= magic_rev then return nil, "bad magic" end
if m ~= magic then
e = ">"
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:read(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: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
}

View File

@ -0,0 +1 @@
return true

View File

@ -0,0 +1,2 @@
---@page unknown "Unknown"
---@doc "This is for if Tsuki doesn't know what BIOS is in use."

View File

@ -0,0 +1,4 @@
return {
name = "Unknown",
version = {0, 0, 0, string="0.0.0"}
}

View File

View File

@ -0,0 +1 @@
return _LOAD == "Zorya"

View File

@ -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."

View File

@ -0,0 +1,5 @@
return {
name = "Zorya",
version = {_ZVER//1, tonumber(tostring(_ZVER):match("%.(%d+)")), _ZPAT, string = _ZVER..".".._ZPAT},
oefiver = oefi.getAPIVersion()
}

View File

@ -0,0 +1 @@
zorya = nil

View File

@ -0,0 +1 @@
return _BIOS == "Zorya NEO"

View File

@ -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."

View File

@ -0,0 +1,5 @@
return {
name = "Zorya NEO",
version = {_ZVER//1, tonumber(tostring(_ZVER):match("%.(%d+)")), _ZPAT, git = _ZGIT, string = _ZVSTR},
loader = _ZLOADER
}

View File

30
ksrc/biosfixes.lua Normal file
View File

@ -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]]

5
ksrc/blk/eeprom.lua Normal file
View File

@ -0,0 +1,5 @@
local eeprom = {}
function eeprom.proxy(addr)
end

0
ksrc/blk/hdd.lua Normal file
View File

0
ksrc/blk/partition.lua Normal file
View File

0
ksrc/blk/promcard.lua Normal file
View File

4
ksrc/blkdev.lua Normal file
View File

@ -0,0 +1,4 @@
--#include "ksrc/blk/eeprom.lua"
--#include "ksrc/blk/hdd.lua"
--#include "ksrc/blk/partition.lua"
--#include "ksrc/blk/promcard.lua"

5
ksrc/buffer.lua Normal file
View File

@ -0,0 +1,5 @@
local buffer = {}
local function create_buffer()
end

34
ksrc/exec.lua Normal file
View File

@ -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

0
ksrc/execs/lua/init.lua Normal file
View File

View File

@ -0,0 +1 @@
--#include "ksrc/execs/velx_spec/velxspec.lua"

View File

@ -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("<H", linkinfo:sub(pos))
pos = pos + 2
local name = linkinfo:sub(pos, pos+size-1)
pos = pos + size
size = string.unpack("<H", linkinfo:sub(pos))
pos = pos + 2
local library = linkinfo:sub(pos, pos+size-1)
pos = pos + size
if (name == "" or library == "") then
break
end
linker[#linker+1] = {name, library}
end
local linkscript = ""
for i=1, #linker do
linkscript = linkscript .. "local " .. linker[i][1] .. "=require(\"" .. linker[i][2] .. "\")\n"
end
self.code = linkscript .. self.code
return true
end
function exec:load()
self.file:seek("set", #velx_spec)
local code = self.file:read(self.header.psize)
if (self.header.compression == 0) then
self.code = code
elseif (self.header.compression == 1) then
self.code = lzss.decompress(self.code)
else
return nil, "invalid compression method"
end
return true
end
function exec:run(args, evar)
system.load(self.code, args, system.getglobalenv({_ARCHIVE=self.arc}), system.getevars(evar))
end

3
ksrc/execs/zlua/init.lua Normal file
View File

@ -0,0 +1,3 @@
local zlua = {}
return {}

1
ksrc/fs/arcfs/init.lua Normal file
View File

@ -0,0 +1 @@
local afs = {}

21
ksrc/fs/foxfs/ads.lua Normal file
View File

@ -0,0 +1,21 @@
function prox:getadses(path)
local inode = fox_getnode(self, path)
local adsl = fox_readdir(self, inode.ads)
return adsl
end
function prox:makeads(path, stream)
end
function prox:getads(path, stream)
local inode = fox_getnode(self, path)
local adsl = fox_readdir(self, inode.ads)
for i=1, do
if (adsl[i].name == stream) then
local node = fox_getinode(self, adsl[i].inode)
return {node=node, pos=1, mode=mode, dev=self}
end
end
return nil, "not found"
end

View File

@ -0,0 +1 @@
local blkstr = {}

14
ksrc/fs/foxfs/dir.lua Normal file
View File

@ -0,0 +1,14 @@
local dirent = struct {
endian="<",
{inode="I4"},
{name_size="H"},
{etype="B"}
}
local function fox_readdir(self, inode)
end
function prox:list(path)
end

View File

@ -0,0 +1,21 @@
function prox:open(path, mode)
local node = fox_getnode(self, path)
if not node then return nil, "file not found" end
return {node=node, pos=1, mode=mode, dev=self}
end
function prox:read(hand, amt)
end
function prox:write(hand, data)
end
function prox:seek(hand, amt)
end
function prox:close(hand)
end

View File

22
ksrc/fs/foxfs/init.lua Normal file
View File

@ -0,0 +1,22 @@
local fox = {}
do
--#include "ksrc/fs/foxfs/inode.lua"
--#include "ksrc/fs/foxfs/dir.lua"
--#include "ksrc/fs/foxfs/superblock.lua"
--#include "ksrc/fs/foxfs/blkstream.lua"
local prox = {}
function fox.proxy(dev)
-- Read the superblock
sb:seek("set", 1025)
local sb = superblock(dev:read(#superblock))
local prox = {
super = sb
}
end
local function fox_getinode(prox, inode)
end
end

33
ksrc/fs/foxfs/inode.lua Normal file
View File

@ -0,0 +1,33 @@
local struct_inode = struct {
endian = "<",
{mode="H"},
{uid="H"},
{fsize="I6"},
{atime="I6"},
{ctime="I6"},
{mtime="I6"},
{dtime="I6"},
{gid="H"},
{sec_count="I4"},
{flags="I4"},
{osval="I4"},
{dbp0="I4"},
{dbp1="I4"},
{dbp2="I4"},
{dbp3="I4"},
{dbp4="I4"},
{dbp5="I4"},
{dbp6="I4"},
{dbp7="I4"},
{dbp8="I4"},
{dbp9="I4"},
{dbp10="I4"},
{dbp11="I4"},
{sibp="I4"},
{dibp="I4"},
{tibp="I4"},
{gen="H"},
{ads="I4"},
{fragaddr="I4"},
{osval2="c10"}
}

View File

View File

@ -0,0 +1,13 @@
local superblock = struct {
endian = "<",
{sig="H"},
{ver_maj="H"},
{ver_min="H"},
{state="H"},
{total_inodes="I4"},
{total_blocks="I4"},
{reserved_blocks="I4"},
{total_unalloc_blocks="I4"},
{total_unalloc_inodes="I4"},
{total_unalloc_inodes="I4"},
}

38
ksrc/init.lua Normal file
View File

@ -0,0 +1,38 @@
--#include "ksrc/kstrings.lua"
--#include "ksrc/kargs.lua"
--#include "ksrc/kio.lua"
--#include "ksrc/vfs.lua"
--#include "ksrc/struct.lua"
--#include "ksrc/string.lua"
--#include "ksrc/archives.lua"
--#include "ksrc/ads.lua"
--#include "ksrc/blkdev.lua"
--#include "ksrc/acl.lua"
--#include "ksrc/security.lua"
--#include "ksrc/exec.lua"
--#include "ksrc/fs/foxfs/init.lua"
--#include "ksrc/tty.lua"
--#include "ksrc/biosfixes.lua"
--#include "ksrc/buffer.lua"
--#include "ksrc/threads.lua"
--#include "ksrc/net.lua"
kio.dmesg(1, "Starting Tsuki kernel.")
kio.init()
-- Mount the initramfs, if there is one.
if (kargs.iramfs or kargs.root == "$") then
vfs.mount("/", arcfs.proxy(_ARCHIVE))
end
-- Eventually...
do
local pty = tty.get(0)
kio.dmesg(1, "Passing off to init.")
exec.startfile(kargs.init or "/bin/init.velx", true, {
uid = 0,
gid = 0,
tty = pty,
args = {kargs}
})
end

0
ksrc/kargs.lua Normal file
View File

299
ksrc/kio.lua Normal file
View File

@ -0,0 +1,299 @@
@[[if not svar.get("PRINT_DMESG_LEVEL") then
svar.set("PRINT_DMESG_LEVEL", "1")
end]]
---@module kio "Kernel I/O"
kio = {}
local _stream = {}
function _stream:read(amt)
local buf = ""
local lc = ""
if (amt == "*l") then -- Our line ending is \n
repeat
lc = self.proto.read(self.udat, 1)
if (lc ~= "\n") then
buf = buf .. (lc or "")
end
until not lc or lc == "" or lc == "\n"
return buf
--elseif (amt == "*n") then
elseif (amt == "*a") then
local pos = self:seek("cur", 0)
local send = self:seek("end", 0)
self:seek("set", pos)
amt = send - pos
end
return self.proto.read(self.udat, amt)
end
function _stream:write(data)
return self.proto.write(self.udat, data)
end
function _stream:seek(whence, amt)
if not amt then
amt = whence or 0
whence = "cur"
end
return self.proto.seek(self.udat, whence, amt)
end
function _stream:eof()
local pos = self:seek("cur", 0)
local send = self:seek("end", 0)
self:seek("set", pos)
return pos == send
end
function _stream:close()
return self.proto.close(self.udat)
end
function _stream:size()
local pos = self:seek("cur", 0)
local send = self:seek("end", 0)
self:seek("set", pos)
return send
end
function kio.create_stream(udat, proto)
return setmetatable({udat=udat, proto=proto}, {__index=_stream})
end
@[[local kio_errc = 0
function kio_err(name, rtn)]]
kio.errno["@[{name}]"] = @[{kio_errc}]
kio_errors[@[{kio_errc}]] = "@[{rtn}]"
@[[kio_errc = kio_errc + 1
end]]
kio.errno = {}
local kio_errors = {}
@[[
kio_err("FILE_NOT_FOUND", "not found")
kio_err("FILE_DIRECTORY", "file is a directory")
kio_err("DEV_TIMEOUT", "device timeout")
kio_err("IO_ERROR", "generic i/o error")
kio_err("UNSUPPORTED_OPERATION", "unsupported operation")
kio_err("NOT_ALLOWED", "not allowed")
kio_err("TOO_MANY_SYMLINKS", "too many symlinks")
kio_err("DEV_FULL", "device is full")
kio_err("DEV_READ_ONLY", "device is read only")
]]
kio.geterror = function(e)
return nil, kio_errors[e] or "generic error"
end
function kio.invoke(path, method, ...)
local dev, rpath = vfs.resolve(path)
return dev.dev[method](dev.dev, rpath, ...)
end
function kio.has_ads(path)
end
function kio.ads_exists(path, ads)
end
function kio.has_acl(path)
end
---@func calc_seek
---@arg whence string "Like io.seek's whence."
---@arg amt integer "The amount to seek."
---@arg size integer "The size of the stream."
---@arg pos integer "The current position."
---@return integer "The new position"
---@desc "This function calculates the new position for seeking."
function kio.calc_seek(whence, amt, size, pos)
if (whence == "cur") then
pos = pos + amt
elseif (whence == "end") then
pos = size + amt
elseif (whence == "set") then
pos = amt
end
if pos > 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

6
ksrc/kstrings.lua Normal file
View File

@ -0,0 +1,6 @@
_KINFO = {
name = "Tsuki",
release = "$[{TSUKI_RELEASE}]",
version = "$[{TSUKI_VERSION}]",
machine = "$[{TSUKI_TARGET}]", -- OC or LuPI2
}

0
ksrc/lupi2.lua Normal file
View File

6
ksrc/net.lua Normal file
View File

@ -0,0 +1,6 @@
--#include "ksrc/net/tsukinet/init.lua"
local net = {}
function net.open(netinfo)
end

View File

20
ksrc/security.lua Normal file
View File

@ -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

6
ksrc/stdlib/err.lua Normal file
View File

@ -0,0 +1,6 @@
_P.pcall = pcall
_P.xpcall = xpcall
_P.error = {}
_P.error.geterror = kio.geterror
_P.error.errno = kio.errno

10
ksrc/stdlib/init.lua Normal file
View File

@ -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"

0
ksrc/stdlib/io.lua Normal file
View File

11
ksrc/stdlib/string.lua Normal file
View File

@ -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]]

9
ksrc/stdlib/table.lua Normal file
View File

@ -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]]

9
ksrc/stdlib/thread.lua Normal file
View File

@ -0,0 +1,9 @@
_P.threads = {}
function _P.threads.add(name, func)
end
function _P.threads.load(path, args)
end

25
ksrc/string.lua Normal file
View File

@ -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

29
ksrc/struct.lua Normal file
View File

@ -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

56
ksrc/threads.lua Normal file
View File

@ -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

15
ksrc/tty.lua Normal file
View File

@ -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

13
ksrc/vfs.lua Normal file
View File

@ -0,0 +1,13 @@
local vfs = {}
function vfs.mount(path, proxy)
end
function vfs.resolve(path)
end
function vfs.umount(pathorproxy)
end

6
readme.md Normal file
View File

@ -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`.

260
utils/gendocs.lua Normal file
View File

@ -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

64
utils/mkvelx.lua Normal file
View File

@ -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()

207
utils/velxdump.lua Normal file
View File

@ -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 <http://unlicense.org/>
--]]----------------------------------------------------------------------------
--------------------------------------------------------------------------------
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