moved stuff to src/ from test/ and made better build scripts

This commit is contained in:
2026-01-15 10:58:27 -05:00
parent 0d46054e56
commit e203f9f36d
84 changed files with 2095 additions and 3618 deletions

View File

@@ -0,0 +1,285 @@
--:Minify:--
function string.hasSuffix(str, suffix)
return string.sub(str, #suffix+1) == suffix
end
function string.hasPrefix(str, prefix)
return string.sub(str, 1, #prefix) == prefix
end
function string.getSuffix(str, prefix)
return string.sub(str, #prefix+1)
end
function string.getPrefix(str, suffix)
return string.sub(str, 1, #suffix)
end
function string.join(str, ...)
return table.concat(table.pack(str, ...))
end
function string.delim(str, ...)
return table.concat(table.pack(...), str)
end
function string.split(str, delim, maxResultCountOrNil)
assert(#delim == 1, "only delim len 1 supported for now")
maxResultCountOrNil = (maxResultCountOrNil or 0)-1
local rv = {}
local buf = ""
for i = 1, #str do
local c = string.sub(str,i,i)
if #rv ~= maxResultCountOrNil and c == delim then
table.insert(rv, buf)
buf = ""
else
buf = buf..c
end
end
table.insert(rv, buf)
return rv
end
function string.replace(str, search, replacement)
local rv = ""
local consumedLen = 1
local i = 1
while i<#str do
if string.sub(str, i, i+#search-1) == search then
rv = rv .. string.sub(str, consumedLen, i-1) .. replacement
i=i+#search
consumedLen = i
end
i=i+1
end
return rv .. string.sub(str, consumedLen)
end
function table.deepcopy(orig, copies)
copies = copies or {}
if type(orig) ~= 'table' then
return orig
elseif copies[orig] then
return copies[orig]
end
local copy = {}
copies[orig] = copy
for k, v in next, orig, nil do
local copied_key = table.deepcopy(k, copies)
local copied_val = table.deepcopy(v, copies)
copy[copied_key] = copied_val
end
return copy
end
function table.hasKey(tabl, query)
for i,v in pairs(tabl) do
if i==query then
return true
end
end
return false
end
function table.hasVal(tabl, query)
for i,v in pairs(tabl) do
if v==query then
return true
end
end
return false
end
function table.proxy(tbl)
local proxies = setmetatable({}, {__mode = "k"}) -- Weak table to avoid cycles
local function createProxy(t)
if type(t) ~= "table" then return t end
if proxies[t] then return proxies[t] end -- reuse proxy for the same table (handle cycles)
local proxy = {}
proxies[t] = proxy
local mt
mt = {
__index = function(_, k)
local value = t[k]
if type(value) == "table" then
return createProxy(value) -- recursively proxy subtables
else
return value
end
end,
__newindex = function()
error("Attempt to modify table proxy", 2)
end,
__pairs = function()
return function(_, k)
local nextKey, nextValue = next(t, k)
if type(nextValue) == "table" then
nextValue = createProxy(nextValue)
end
return nextKey, nextValue
end, nil, nil
end,
__ipairs = function()
local i = 0
local n = #t
return function()
i = i + 1
if i <= n then
local v = t[i]
if type(v) == "table" then
v = createProxy(v)
end
return i, v
end
end
end,
__metatable = false
}
setmetatable(proxy, mt)
return proxy
end
return createProxy(tbl)
end
local function serialize(table)
local output = "{"
for i,v in pairs(table) do
local coma=true
if type(i) == "string" then
output=output.."[\""..i.."\"]="
elseif type(i) == "number" then
output=output.."["..tostring(i).."]="
end
if type(v) == "table" then
if v == table then
output=string.sub(output,1,#output-(#i+1))
coma=false
else
output=output..serialize(v)
end
elseif type(v) == "string" then
output=output.."[=["..v.."]=]"
elseif type(v) == "number" then
output=output..tostring(v)
elseif type(v) == "boolean" then
if v == true then
output=output.."true"
else
output=output.."false"
end
elseif type(v) == "function" then
output=output..tostring(v)
else
error("serialization of type \""..type(v).."\" is not supported")
end
if coma then
output=output..","
end
end
if #table>0 or string.sub(output,#output,#output) == "," then
output=string.sub(output,1,#output-1)
end
output=output.."}"
return output
end
local oldtype=type
local oldgetmetatable=getmetatable
function type(object, trueType)
if trueType then
return oldtype(object)
end
if oldtype(object)~="table" then
return oldtype(object)
else
if oldtype(oldgetmetatable(object))=="table" then
local metatable = oldgetmetatable(object)
if metatable.__type then return metatable.__type end
else
return "table"
end
end
end
function getmetatable(object)
if oldtype(object)~="table" then return end
if oldtype(oldgetmetatable(object))=="table" then
if oldgetmetatable(object).__isuserdata then
if oldtype(oldgetmetatable(object).__usermeta)=="function" then
return oldgetmetatable(object).__usermeta()
else
return oldgetmetatable(object).__usermeta
end
else
return oldgetmetatable(object)
end
else
return oldgetmetatable(object)
end
end
function isEqualToAny(a, ...)
local args={...}
for i=0, #args do
if a==args[i] then
return true
end
end
return false
end
function isEqualToAll(a, ...)
local args={...}
for i=0, #args do
if a~=args[i] then
return false
end
end
return true
end
function table.keys(t)
local a = {}
for n in pairs(t) do table.insert(a, n) end
return a
end
function table.values(t)
local a = {}
for _, n in pairs(t) do table.insert(a, n) end
return a
end
function table.indexOf(t, value)
for i,v in ipairs(t) do
if v==value then
return i
end
end
return -1
end
syscall = setmetatable({}, {
__index = function(self, name)
return function(...)
local res = table.pack(coroutine.yield("syscall", name, ...))
if res[1] then
return table.unpack(res, 2, res.n)
else
error(res[2], 2)
end
end
end
})
table.serialize=serialize

View File

@@ -0,0 +1,278 @@
--:Minify:--
local kernel = ...
local vfs = {}
vfs.mounts = { ["$"] = "/" }
local disks = kernel.disks
-- Path handling
local function normalizePath(path)
local parts = {}
for part in path:gmatch("[^/]+") do
if part == ".." then
if #parts > 0 then table.remove(parts) end
elseif part ~= "." and part ~= "" then
table.insert(parts, part)
end
end
return "/" .. table.concat(parts, "/") .. (path:sub(-1) == "/" and "/" or "")
end
local function resolvePath(path)
local task = kernel.currentTask
local cwd = task.cwd or "/"
if path:sub(1,1) ~= "/" then
path = cwd .. path
end
path = normalizePath(path)
local mountPoint = "/"
local mountId = "$"
for k,v in pairs(vfs.mounts) do
if path:sub(1,#v) == v and #v > #mountPoint then
mountPoint = v
mountId = k
end
end
local diskPath = path:sub(#mountPoint + 1)
return disks[mountId], diskPath
end
-- File object creation
local function newFileObject(disk, handle, mode, path)
return {
disk = disk,
handle = handle,
mode = mode,
path = path,
refcount = 1
}
end
local function allocFD(task)
local fd = 0
while task.fd[fd] do fd = fd + 1 end
return fd
end
local function allocFD(task)
-- enforce per-task limit
local count = 0
for _ in pairs(task.fd) do count = count + 1 end
if count >= kernel.config.maxFilesPerTask then
error("EMFILE") -- Too many open files in this task
end
-- find first free FD
local fd = 0
while task.fd[fd] do fd = fd + 1 end
return fd
end
local function checkSystemLimit()
-- enforce system-wide limit
local total = 0
for _, task in pairs(kernel.tasks or {}) do
for _ in pairs(task.fd) do total = total + 1 end
end
if total >= kernel.config.maxOpenFiles then
error("ENFILE") -- Too many open files in the system
end
end
-- VFS syscalls
function vfs.open(path, mode)
local task = kernel.currentTask
-- check limits
checkSystemLimit()
local disk, diskPath = resolvePath(path)
if not disk then
error("No disk mounted for path '"..path.."'")
end
local handle = disk:open(diskPath, mode)
if not handle then return nil end
local file = newFileObject(disk.address, handle, mode, path)
local fd = allocFD(task)
task.fd[fd] = file
return fd
end
function vfs.close(fd)
local task = kernel.currentTask
local file = task.fd[fd]
if not file then error("EBADF") end
task.fd[fd] = nil
file.refcount = file.refcount - 1
if file.refcount == 0 then
file.handle.close()
end
return true
end
function vfs.read(fd, count)
local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end
if not file.mode:find("r") then error("File not open for reading") end
return file.handle.read(count)
end
function vfs.write(fd, data)
local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end
if not file.mode:find("w") then error("File not open for writing") end
return file.handle.write(data)
end
function vfs.whereis(fd)
local file = kernel.currentTask.fd[fd]
if not file then error("EBADF") end
return file.path
end
-- Filesystem operations
function vfs.mkdir(path)
local disk, diskPath = resolvePath(path)
if not disk then error("No disk mounted") end
return disk:makeDirectory(diskPath)
end
function vfs.remove(path)
local disk, diskPath = resolvePath(path)
if not disk then error("No disk mounted") end
return disk:remove(diskPath)
end
function vfs.attributes(path)
if type(path) == "number" then
local file = kernel.currentTask.fd[path]
if not file then error("EBADF") end
return disks[file.disk]:attributes(file.path)
end
local disk, diskPath = resolvePath(path)
if not disk then error("No disk mounted") end
return disk:attributes(diskPath)
end
function vfs.list(path)
local disk, diskPath = resolvePath(path)
if not disk then error("No disk mounted") end
return disk:list(diskPath)
end
function vfs.exists(path)
local disk, diskPath = resolvePath(path)
if not disk then return false end
return disk:directoryExists(diskPath) or disk:fileExists(diskPath)
end
function vfs.type(path)
if type(path) == "number" then
local file = kernel.currentTask.fd[path]
if not file then error("EBADF") end
return disks[file.disk]:type(file.path)
end
local disk, diskPath = resolvePath(path)
if not disk then error("No disk mounted") end
return disk:type(diskPath)
end
-- CWD
function vfs.getcwd()
return kernel.currentTask.cwd
end
function vfs.setcwd(path)
if path:sub(-1) ~= "/" then path = path .. "/" end
if path:sub(1,1) ~= "/" then path = "/" .. path end
kernel.currentTask.cwd = normalizePath(path)
end
-- Mounting
function vfs.mount(diskId, path)
if kernel.uid ~= 0 then error("Permission denied") end
if not disks[diskId] then error("Unknown disk '"..diskId.."'") end
if path:sub(-1) ~= "/" then path = path .. "/" end
for _,v in pairs(vfs.mounts) do
if v == path then error("Mount point already used") end
end
vfs.mounts[diskId] = path
end
function vfs.unmount(path)
if kernel.uid ~= 0 then error("Permission denied") end
for k,v in pairs(vfs.mounts) do
if v == path then
vfs.mounts[k] = nil
return true
end
end
return false
end
function vfs.getMounts()
return vfs.mounts
end
function vfs.virtdisk(obj)
kernel.disks[obj.address] = obj
kernel.log("Registered virtual disk at "..obj.address)
end
-- Redirect file operations to VFS
function vfs.dup(oldfd)
local task = kernel.currentTask
local file = task.fd[oldfd]
if not file then error("EBADF") end
local newfd = allocFD(task)
task.fd[newfd] = file
file.refcount = file.refcount + 1
return newfd
end
function vfs.dup2(oldfd, newfd)
local task = kernel.currentTask
local file = task.fd[oldfd]
if not file then error("EBADF") end
if oldfd == newfd then return newfd end
if task.fd[newfd] then
vfs.close(newfd)
end
task.fd[newfd] = file
file.refcount = file.refcount + 1
return newfd
end
-- Syscall registration
kernel.vfs = vfs
kernel.syscalls["VFS_open"] = vfs.open
kernel.syscalls["VFS_read"] = vfs.read
kernel.syscalls["VFS_write"] = vfs.write
kernel.syscalls["VFS_close"] = vfs.close
kernel.syscalls["VFS_list"] = vfs.list
kernel.syscalls["VFS_type"] = vfs.type
kernel.syscalls["VFS_attributes"] = vfs.attributes
kernel.syscalls["VFS_mkdir"] = vfs.mkdir
kernel.syscalls["VFS_remove"] = vfs.remove
kernel.syscalls["VFS_exists"] = vfs.exists
kernel.syscalls["VFS_mount"] = vfs.mount
kernel.syscalls["VFS_unmount"] = vfs.unmount
kernel.syscalls["VFS_getcwd"] = vfs.getcwd
kernel.syscalls["VFS_setcwd"] = vfs.setcwd
kernel.syscalls["VFS_whereis"] = vfs.whereis
kernel.syscalls["VFS_dup"] = vfs.dup
kernel.syscalls["VFS_dup2"] = vfs.dup2
kernel.log("VFS module loaded")

View File

@@ -0,0 +1,43 @@
--:Minify:--
local kernel = ...
local cache = {}
local searchpaths = {
"/lib/?.lua",
"/lib/?",
"/usr/lib/?.lua",
"/usr/lib/?",
"/usr/local/lib/?.lua",
"/usr/local/lib/?",
"?.lua",
"?"
}
function require(module,...)
if cache[module] then
return cache[module]
end
local modpath = module:gsub("%.", "/")
local failed = {}
for _, path in ipairs(searchpaths) do
local full_path = string.gsub(path, "%?", modpath)
if full_path:sub(1,1)~="/" then
full_path=kernel.currentTask.cwd..full_path
end
if kernel.vfs.exists(full_path) then
if kernel.vfs.type(full_path)=="directory" then
full_path = full_path .. "/init"
end
if kernel.vfs.exists(full_path) then
local handle = kernel.vfs.open(full_path, "r")
local file_content = kernel.vfs.read(handle, 1024 * 1024 * 4)
kernel.vfs.close(handle)
return assert(load(file_content, full_path, "t", kernel._U))(...)
else
table.insert(failed, full_path)
end
else
table.insert(failed, full_path)
end
end
error("Module not found: " .. module .. " (searched paths: " .. table.concat(failed, ", ") .. ")")
end

View File

@@ -0,0 +1,173 @@
--:Minify:--
local kernel = ...
local proxy = {}
local data = {}
proxy.address = "devfs0000"
proxy.isReadOnly = false
proxy.spaceUsed = function() return 0 end
proxy.spaceTotal = function() return 0 end
proxy.makeDirectory = function() error("Permission denied") end
proxy.remove = function() error("Permission denied") end
proxy.setLabel = function() error("Permission denied") end
proxy.getLabel = function() return "devfs" end
proxy.attributes = function(path) return {
type = proxy.type(path),
isReadOnly = false,
size = 0,
lastModified = 0,
created = 0,
Permissions = "666",
owner = "root",
group = "root"
} end
local function getNode(path)
local parts = string.split(path, "/")
if parts[1] == "" then
table.remove(parts, 1)
end
local node = data
for _, part in ipairs(parts) do
if node[part] then
node = node[part]
else
return nil
end
end
return node
end
proxy.type = function(path)
local node = getNode(path)
if node then
return node.type
else
return nil
end
end
proxy.list = function(path)
local node = getNode(path)
if node and node.type == "directory" then
local content = table.keys(node)
table.remove(content, table.indexOf(content, "type"))
return content
else
error("Not a directory")
end
end
proxy.open = function(path, mode)
local node = getNode(path)
if node and (node.type == "file" or node.type == "character device") then
if mode == "r" then
return {
read = node.read,
close = function() end
}
elseif mode == "w" then
return {
write = node.write,
close = function() end
}
else
error("Invalid mode")
end
else
error("Not a file"..type(node))
end
end
local function newStringFile(content)
return {
type = "file",
read = function() return content end,
write = function(newContent) content = newContent end
}
end
local function newDirectory()
return {
type = "directory"
}
end
data["random"] = {
type = "character device",
read = function(amount)
local result = ""
for _ = 1, amount do
result = result .. string.char(math.random(0, 255))
end
return result
end,
write = function() error("Permission denied") end
}
data["null"] = {
type = "character device",
read = function() return "" end,
write = function() end
}
data["zero"] = {
type = "character device",
read = function(amount)
return string.rep("\0", amount)
end,
write = function() error("Permission denied") end
}
data["rtc"] = {
type = "character device",
read = function() return kernel.computer:time() end,
write = function() error("Permission denied") end
}
data["rtc0"] = {
type = "character device",
read = function() return kernel.computer:time() end,
write = function() error("Permission denied") end
}
data["eeprom"] = {
type = "character device",
read = function() return kernel.computer:getEEPROM() end,
write = function(data)
if kernel.uid ~= 0 then
error("Permission denied")
end
kernel.computer:setEEPROM(data)
end
}
local keyboard = kernel.newFifo()
local mouse = kernel.newFifo()
data["input"] = newDirectory()
data["input"]["keyboard"] = {
type = "pipe",
read = function(amount)
return keyboard.pop()
end,
write = function() error("Permission denied") end
}
data["input"]["mouse"] = {
type = "pipe",
read = function(amount)
return mouse.pop()
end,
write = function() error("Permission denied") end
}
data["pts"] = newDirectory()
kernel.devfs = {}
kernel.devfs.keyboard = keyboard
kernel.devfs.mouse = mouse
kernel.devfs.proxy = proxy
kernel.devfs.data = data
kernel.vfs.virtdisk(proxy)

View File

@@ -0,0 +1,13 @@
--:Minify:--
local kernel = ...
kernel.processes.keventd = function()
while true do
local event = {kernel.computer:getMachineEvent()}
if event[1] then
if event[1] == "key" or event[1] == "keyPressed" or event[1] == "keyReleased" then
kernel.devfs.keyboard.push(event)
end
end
end
end

View File

@@ -0,0 +1,18 @@
--:Minify:--
local kernel=...
for i,v in ipairs(string.split(kernel.fstab,"\n")) do
if v:sub(1,1)=="U" then
local id=""
for i=3,#v do
if v:sub(i,i)==";" then
if i==3 then kernel.log("Invalid fstab line... Skipping.","WARN") goto endline end
id=v:sub(3,i-1)
end
end
local path=v:sub(#id+4)
if id~="$" then
kernel.vfs.mount(id,path)
end
::endline::
end
end

View File

@@ -0,0 +1,76 @@
--:Minify:--
local kernel = ...
local ports = {}
local signals = {}
local ipc = {}
function ipc.open(port)
if not ports[port] then
local handle = kernel.newUUID()
ports[port] = {owner = kernel.currentProcess.pid, handle = handle, messages = {}}
return ports[port].handle
end
error("Port already opened")
end
function ipc.close(port)
if ports[port] then
if ports[port].owner == kernel.currentProcess.pid then
ports[port] = nil
return true
else
error("Cannot close port you do not own")
end
end
error("Port not opened")
end
function ipc.send(port, message)
if ports[port] then
table.insert(ports[port].messages, {from = kernel.currentProcess.pid, message = message})
if signals[ports[port].owner] then
signals[ports[port].owner](port, message)
end
return true
end
error("Port not opened")
end
function ipc.receive(port)
if ports[port] then
if #ports[port].messages > 0 then
return table.remove(ports[port].messages, 1)
else
return nil
end
end
error("Port not opened")
end
function ipc.setSignalHandler(pid, handler)
signals[pid] = handler
end
function ipc.clearSignalHandler(pid)
signals[pid] = nil
end
function ipc.sendSignal(pid, ...)
coroutine.resumeWithTimeout(coroutine.create(function(...)
if signals[pid] then
signals[pid](...)
return true
end
end), 100)
return false
end
kernel.ipc = ipc
kernel.syscalls["ipc_open"] = ipc.open
kernel.syscalls["ipc_close"] = ipc.close
kernel.syscalls["ipc_send"] = ipc.send
kernel.syscalls["ipc_receive"] = ipc.receive
kernel.syscalls["ipc_setSignalHandler"] = ipc.setSignalHandler
kernel.syscalls["ipc_clearSignalHandler"] = ipc.clearSignalHandler
kernel.syscalls["ipc_sendSignal"] = ipc.sendSignal
kernel.log("Loaded IPC module")

View File

@@ -0,0 +1,18 @@
--:Minify:--
local args={...}
local kernel=args[1]
kernel._G=_G
kernel._U=setmetatable({},{
__index = kernel._G,
__newindex = function(t,k,v)
if kernel.config.allowGlobalOverwrites or kernel.allowGlobalOverwrites then
rawset(t,k,v)
return
end
error("Attempt to modify global variable '"..k.."'",2)
end,
__metatable = false
})
kernel.allowGlobalOverwrites=true
kernel._U._G=kernel._U
kernel.allowGlobalOverwrites=false

View File

@@ -0,0 +1,66 @@
--:Minify:--
local kernel = ...
local pam = {}
kernel.pam = pam
local loggedIn = {}
local function getFile(path)
local file = kernel.vfs.open(path, "r")
if not file then error("Failed to open file: "..path) end
local content = kernel.vfs.read(file, 1024000)
kernel.vfs.close(file)
return content
end
local blake2s = require("crypto.blake2s")
if not blake2s then error("Failed to load blake2s") end
if not kernel.vfs.exists("/etc/pam.d/secret") then
local key = ""
for i=1, 256 do
key=key..string.char(math.random(1,255))
end
local handle = kernel.vfs.open("/etc/pam.d/secret", "w")
kernel.vfs.write(handle, key)
kernel.vfs.close(handle)
end
local pepper = getFile("/etc/pam.d/secret")
function pam.authenticate(username, password)
local fpasswd = getFile("/etc/passwd")
local fshadow = getFile("/etc/shadow")
local passwdLines = string.split(fpasswd, "\n")
local shadowLines = string.split(fshadow, "\n")
local passwd = {}
local shadow = {}
for _, line in ipairs(passwdLines) do
local fields = string.split(line, ":")
passwd[fields[1]] = fields
end
for _, line in ipairs(shadowLines) do
local fields = string.split(line, ":")
shadow[fields[1]] = fields
end
for user, fields in pairs(passwd) do
if user == username then
local shadowPasswd = string.split(shadow[user][2], "$")
local salt = shadowPasswd[2]
local hashedPassword = blake2s(password .. salt, pepper)
if hashedPassword == shadowPasswd[3] then
loggedIn[username] = kernel.newUUID()
return loggedIn[username]
else
return false
end
end
end
end
function pam.authToken(username, token)
return loggedIn[username] == token
end

View File

@@ -0,0 +1,212 @@
--:Minify:--
local kernel = ...
local tasks = {}
local sys = {}
local nextpid = 2
kernel.exitMain=false
function sys.spawn(func, name, envars, args, tgid, fshandles)
local id = nextpid
nextpid = nextpid + 1
tasks[tostring(id)] = {
coro=coroutine.create(function()
local ok, err = xpcall(func, debug.traceback, table.unpack(args or {}))
if not ok then
tasks[tostring(id)].status="Z"
tasks[tostring(id)].exit=tostring(err)
else
tasks[tostring(id)].status="Z"
tasks[tostring(id)].exit=err
end
end),
name=name or "task"..tostring(id),
envars=envars or {},
args=args or {},
status="R",
pid=id,
tgid=tgid or kernel.currentTask.tgid,
user=kernel.user,
uid=kernel.uid,
fd=fshandles or {},
exit="",
sleep=0,
ivs=0,
vs=0,
children={},
parent=kernel.currentTask,
siblings=kernel.currentTask.children,
syscallReturn={},
cwd=kernel.currentTask.cwd,
term=kernel.currentTask.term,
timeSlice=0,
lastTime=0,
totalTime=0,
numRuns=0
}
table.insert(kernel.currentTask.children, tasks[tostring(id)])
end
function sys.sleep(ms)
kernel.currentTask.status="S"
kernel.currentTask.sleep=kernel.computer:time()+ms
coroutine.yield()
end
kernel.syscalls["HPV_spawn"]=sys.spawn
kernel.syscalls["HPV_sleep"]=sys.sleep
kernel._G.sleep=function(...)coroutine.yield("syscall","HPV_sleep",...)end
local function reapDeadTasks()
for pid, task in pairs(tasks) do
if task.status == "Z" and not task.reapTime then
task.coro = nil
task.ivs = nil
task.vs = nil
task.args = nil
task.envars = nil
task.cwd = nil
task.term = nil
task.numRuns = nil
task.totalTime = nil
task.lastTime = nil
task.timeSlice = nil
task.syscallReturn = nil
task.sleep = nil
for k,v in pairs(task.fd) do
kernel.vfs.close(v)
end
task.fd = nil
task.reapTime = kernel.computer:time() + 30000
elseif task.reapTime and kernel.computer:time() > task.reapTime then
for _,child in ipairs(task.children) do
child.parent = tasks["1"]
child.siblings = tasks["1"].children
table.insert(tasks["1"].children, child)
end
for i, sibling in ipairs(task.siblings) do
if sibling.pid == task.pid then
table.remove(task.siblings, i)
break
end
end
tasks[pid] = nil
end
end
end
local alpha = 0.85
local C_target = 0.01
local Tmin = 0.0005
local Tmax = 0.5
local lambda_budget = 0.08
local lambda_clamp = 0.03
local lambda_var = 0.02
local k_min = 0.5
local k_max = 0.5
local B = 0.01
function kernel.main()
while not kernel.exitMain do
local N = 0
local Tmin_hit = 0
local Tmax_hit = 0
local totalTaskTime = 0
local taskTimes = {}
for pid, task in pairs(tasks) do
if task.status == "S" then
if kernel.computer:time() >= task.sleep then
task.status="R"
task.sleep=0
end
end
if task.status == "R" then
kernel.currentTask = task
kernel.vfs.cwd = task.cwd
kernel.user = task.user
kernel.uid = task.uid
N = N + 1
-- assign adaptive time slice
task.timeSlice = math.min(Tmax, math.max(Tmin, B / (N ^ alpha)))
-- measure execution time
local startTime = kernel.computer:time()
local ret
if kernel.config.preempt then
ret = {coroutine.resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn))}
else
ret = {coroutine.resume(task.coro, table.unpack(task.syscallReturn))}
end
local elapsed = kernel.computer:time() - startTime
task.lastTime = elapsed
task.totalTime = (task.totalTime or 0) + elapsed
task.numRuns = (task.numRuns or 0) + 1
taskTimes[#taskTimes + 1] = elapsed
totalTaskTime = totalTaskTime + elapsed
if elapsed <= Tmin then Tmin_hit = Tmin_hit + 1 end
if elapsed >= Tmax then Tmax_hit = Tmax_hit + 1 end
-- handle task results
if ret[1] == "error" then
kernel.log("processHandlerException: "..ret[2])
task.status = "Z"
task.exit = "processHandlerException: "..ret[2]
elseif ret[1] == "timeout" then
task.ivs=task.ivs+1
task.syscallReturn = {}
elseif ret[1] == "success" then
task.vs=task.vs+1
if ret[2]=="syscall" then
if kernel.syscalls[ret[3]] then
if kernel.config.debugSyscalls then
kernel.log("Task "..task.pid.." invoking syscall: "..ret[3], "DBUG")
end
local sysret = {xpcall(kernel.syscalls[ret[3]], debug.traceback, table.unpack(ret, 4))}
if kernel.config.debugSyscalls then
if not sysret[1] then
kernel.log("Task "..task.pid.." syscall "..ret[3].." failed: "..tostring(sysret[2]))
else
kernel.log("Task "..task.pid.." syscall "..ret[3].." completed returning "..tostring(#sysret-1).." values", "DBUG")
for i=2,#sysret do
kernel.log(" retval["..tostring(i-1).."] = "..tostring(sysret[i]), "DBUG")
end
end
end
if not sysret[1] then
task.syscallReturn={false, sysret[2]}
else
task.syscallReturn={true, table.unpack(sysret,2)}
end
else
task.syscallReturn={false, "Unknown syscall: "..tostring(ret[3])}
end
end
end
end
end
local T_prev_avg = (N > 0) and (totalTaskTime / N) or 0
local T_prev_var = 0
for _, t in ipairs(taskTimes) do
T_prev_var = T_prev_var + (t - T_prev_avg)^2
end
if N > 0 then T_prev_var = T_prev_var / N end
if N > 0 then
local f_clamp = k_min*(Tmin_hit/N) - k_max*(Tmax_hit/N)
local B_budget = (C_target * (N^(alpha-1))) / math.max(T_prev_avg, 1e-8)
B = B + lambda_budget * (B_budget - B)
+ lambda_clamp * f_clamp
- lambda_var * T_prev_var
end
-- clean up dead tasks
reapDeadTasks()
end
end
kernel.tasks=tasks
kernel.hpv=sys

View File

@@ -0,0 +1,7 @@
--:Minify:--
local kernel=...
local debug=debug
kernel._G.debug={
getinfo=debug.getinfo,
traceback=debug.traceback
}

View File

@@ -0,0 +1,126 @@
--:Minify:--
local kernel=...
kernel.tty={}
function kernel.tty.register(tty, ttyo)
kernel.tty[tty]=ttyo
end
function kernel.tty.print(text)
local term=kernel.currentTask.term
if term and kernel.tty[term] then
kernel.tty[term].print(text)
end
end
function kernel.tty.printInline(text)
local term=kernel.currentTask.term
if term and kernel.tty[term] then
kernel.tty[term].printInline(text)
end
end
function kernel.tty.size()
local term=kernel.currentTask.term
if term and kernel.tty[term] then
return kernel.tty[term].size()
end
end
function kernel.tty.setCursorPos(x,y)
local term=kernel.currentTask.term
if term and kernel.tty[term] then
return kernel.tty[term].setCursorPos(x,y)
end
end
function kernel.tty.getCursorPos()
local term=kernel.currentTask.term
if term and kernel.tty[term] then
return kernel.tty[term].getCursorPos()
end
end
function kernel.tty.clear()
local term=kernel.currentTask.term
if term and kernel.tty[term] then
return kernel.tty[term].clear()
end
end
function kernel.tty.setTextColor(color)
local term=kernel.currentTask.term
if term and kernel.tty[term] then
return kernel.tty[term].setTextColor(color)
end
end
function kernel.tty.setBackgroundColor(color)
local term=kernel.currentTask.term
if term and kernel.tty[term] then
return kernel.tty[term].setBackgroundColor(color)
end
end
function kernel.tty.scroll(n)
local term=kernel.currentTask.term
if term and kernel.tty[term] then
return kernel.tty[term].scroll(n)
end
end
function kernel.tty.getTextColor()
local term=kernel.currentTask.term
if term and kernel.tty[term] and kernel.tty[term].getTextColor then
return kernel.tty[term].getTextColor()
end
end
function kernel.tty.getBackgroundColor()
local term=kernel.currentTask.term
if term and kernel.tty[term] and kernel.tty[term].getBackgroundColor then
return kernel.tty[term].getBackgroundColor()
end
end
function kernel.tty.bind(ttyid)
if not ttyid then
return false, "No TTY ID specified"
end
if not kernel.tty[ttyid] then
return false, "TTY "..tostring(ttyid).." not registered"
end
kernel.currentTask.term=ttyid
return true
end
function kernel.tty.unbind()
kernel.currentTask.term=false
end
function kernel.tty.isBound()
return kernel.currentTask.term ~= nil
end
function kernel.tty.getBoundTTY()
return kernel.currentTask.term
end
kernel.syscalls["TTY_print"]=kernel.tty.print
kernel.syscalls["TTY_printInline"]=kernel.tty.printInline
kernel.syscalls["TTY_size"]=kernel.tty.size
kernel.syscalls["TTY_setCursorPos"]=kernel.tty.setCursorPos
kernel.syscalls["TTY_getCursorPos"]=kernel.tty.getCursorPos
kernel.syscalls["TTY_clear"]=kernel.tty.clear
kernel.syscalls["TTY_setTextColor"]=kernel.tty.setTextColor
kernel.syscalls["TTY_setBackgroundColor"]=kernel.tty.setBackgroundColor
kernel.syscalls["TTY_scroll"]=kernel.tty.scroll
kernel.syscalls["TTY_getTextColor"]=kernel.tty.getTextColor
kernel.syscalls["TTY_getBackgroundColor"]=kernel.tty.getBackgroundColor
kernel.syscalls["TTY_bind"]=kernel.tty.bind
kernel.syscalls["TTY_unbind"]=kernel.tty.unbind
kernel.syscalls["TTY_isBound"]=kernel.tty.isBound
kernel.syscalls["TTY_getBoundTTY"]=kernel.tty.getBoundTTY
kernel.log("TTY module loaded attempting to register console tty")
kernel.status="init"

View File

@@ -0,0 +1,3 @@
--:Minify:--
local kernel=...
kernel.tty.bind("tty0")

View File

@@ -0,0 +1,29 @@
--:Minify:--
local kernel=...
function print(...)
coroutine.yield()
local args={...}
local output=""
for i=1,#args do
output=output..tostring(args[i]).."\t"
end
output=output:sub(1,-2)
kernel.tty.print(output)
end
function printf(fmt, ...)
coroutine.yield()
local output=string.format(fmt,...)
kernel.tty.print(output)
end
function printInline(...)
coroutine.yield()
local args={...}
local output=""
for i=1,#args do
output=output..tostring(args[i]).."\t"
end
output=output:sub(1,-2)
kernel.tty.printInline(output)
end

View File

@@ -0,0 +1,45 @@
--:Minify:--
local kernel = ...
kernel.log("Loading init system...")
kernel.log("InitPath: "..kernel.config.initPath)
local handle = kernel.vfs.open(kernel.config.initPath, "r")
local data = kernel.vfs.read(handle, 1024 * 1024 * 4)
kernel.vfs.close(handle)
local initFunc, err = load(data, "@sysinit")
if not initFunc then
error("Failed to load init system: "..err)
end
kernel.tasks["1"] = {
coro=coroutine.create(function()
local ok, err = xpcall(initFunc, debug.traceback, kernel)
if not ok then
kernel.panic("Init system crashed: "..tostring(err))
else
kernel.panic("Init system exited: "..tostring(err))
end
end),
name="sysinit",
status="R",
pid=1,
tgid=1,
user="root",
uid=0,
fd={},
exit="",
sleep=0,
ivs=0,
vs=0,
parent=kernel.kernelTask,
siblings=kernel.kernelTask.children,
children={},
syscallReturn={},
cwd="/",
timeSlice=0,
lastTime=0,
totalTime=0,
numRuns=0
}
kernel.log("created init task with PID 1")
kernel.log("Initializing init system...")