forked from Hyperion/HyperionOS
moved stuff to src/ from test/ and made better build scripts
This commit is contained in:
2
Src/Hyperion-kernel-v1.0.0/boot/fstab
Normal file
2
Src/Hyperion-kernel-v1.0.0/boot/fstab
Normal file
@@ -0,0 +1,2 @@
|
||||
U $;/
|
||||
U devfs0000;/dev/
|
||||
78
Src/Hyperion-kernel-v1.0.0/boot/initfs
Normal file
78
Src/Hyperion-kernel-v1.0.0/boot/initfs
Normal file
@@ -0,0 +1,78 @@
|
||||
--:Minify:--
|
||||
local fs = {}
|
||||
local disks = {}
|
||||
local mounts = {}
|
||||
|
||||
local function resolve(path)
|
||||
local mountPoint = "/"
|
||||
for mount, disk in pairs(mounts) do
|
||||
if path:sub(1, #mount) == mount then
|
||||
if not mountPoint or #mount > #mountPoint then
|
||||
mountPoint = mount
|
||||
end
|
||||
end
|
||||
end
|
||||
local newPath = path:sub(#mountPoint + 1)
|
||||
return disks[mounts[mountPoint]], newPath
|
||||
end
|
||||
---------------------------------------------------------
|
||||
|
||||
function fs.update(initdisks)
|
||||
disks = {}
|
||||
for k, v in initdisks.list() do
|
||||
disks[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function fs.exists(path)
|
||||
local disk, newPath = resolve(path)
|
||||
return disk:directoryExists(newPath) or disk:fileExists(newPath)
|
||||
end
|
||||
function fs.isFile(path)
|
||||
local disk, newPath = resolve(path)
|
||||
return disk:fileExists(newPath)
|
||||
end
|
||||
function fs.isDir(path)
|
||||
local disk, newPath = resolve(path)
|
||||
return disk:directoryExists(newPath)
|
||||
end
|
||||
function fs.list(path)
|
||||
local disk, newPath = resolve(path)
|
||||
return disk:list(newPath)
|
||||
end
|
||||
function fs.makeDir(path)
|
||||
local disk, newPath = resolve(path)
|
||||
return disk:makeDirectory(newPath)
|
||||
end
|
||||
function fs.remove(path)
|
||||
local disk, newPath = resolve(path)
|
||||
return disk:remove(newPath)
|
||||
end
|
||||
function fs.readAllText(path)
|
||||
local disk, newPath = resolve(path)
|
||||
local handle = disk:open(newPath, "r")
|
||||
if not handle then return nil end
|
||||
local content = handle.readAll()
|
||||
handle.close()
|
||||
return content
|
||||
end
|
||||
function fs.writeAllText(path, text)
|
||||
local disk, newPath = resolve(path)
|
||||
local handle = disk:open(newPath, "w")
|
||||
handle.write(text)
|
||||
handle.close()
|
||||
end
|
||||
function fs.appendAllText(path, text)
|
||||
local disk, newPath = resolve(path)
|
||||
local handle = disk:open(newPath, "a")
|
||||
handle.write(text)
|
||||
handle.close()
|
||||
end
|
||||
function fs.load(path)
|
||||
return load(fs.readAllText(path), path)
|
||||
end
|
||||
function fs.mount(disk, mountPoint)
|
||||
if not disks[disk] then return end
|
||||
mounts[mountPoint] = disk
|
||||
end
|
||||
return fs
|
||||
243
Src/Hyperion-kernel-v1.0.0/boot/kernel.lua
Normal file
243
Src/Hyperion-kernel-v1.0.0/boot/kernel.lua
Normal file
@@ -0,0 +1,243 @@
|
||||
--:Minify:--
|
||||
local args = {...}
|
||||
local apis = args[1]
|
||||
local disks = args[2]
|
||||
local arch = args[3]
|
||||
local screen = args[5]
|
||||
local computer = args[6]
|
||||
local ifs = args[7]
|
||||
local LOG_Text = ""
|
||||
local kernel = {}
|
||||
kernel.process = "Kernel"
|
||||
kernel.user = "root"
|
||||
kernel.group = "root"
|
||||
kernel.groups = {0}
|
||||
kernel.uid = 0
|
||||
kernel.gid = 0
|
||||
kernel.status = "start"
|
||||
kernel.key = {}
|
||||
kernel.cache = {}
|
||||
kernel.cache.preload = {}
|
||||
kernel._G=_G
|
||||
kernel.sleep=sleep
|
||||
kernel.debug=true
|
||||
|
||||
_G.sleep=nil
|
||||
local windowsExp = false
|
||||
|
||||
function kernel.log(msg, level)
|
||||
LOG_Text = LOG_Text..tostring(computer:time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg.."\n"
|
||||
if kernel.status == "start" then
|
||||
screen:print(tostring(computer:time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||
elseif kernel.status == "init" then
|
||||
kernel.standbyTask=kernel.currentTask
|
||||
kernel.currentTask=kernel.kernelTask
|
||||
kernel.tty.print(tostring(computer:time()).." "..kernel.user.." "..kernel.process.."["..tostring(level or "INFO").."]: "..msg)
|
||||
kernel.currentTask=kernel.standbyTask
|
||||
end
|
||||
end
|
||||
|
||||
function kernel.PANIC(msg)
|
||||
if kernel.status~="Panic" then
|
||||
kernel.log("PANIC: "..msg, "PANIC")
|
||||
pcall(kernel["saveLog"])
|
||||
kernel.status="Panic"
|
||||
kernel.reason=msg
|
||||
screen:setTextColor(2)
|
||||
screen:setBackgroundColor(0)
|
||||
screen:clear()
|
||||
screen:setCursorPos(1,1)
|
||||
screen:print(LOG_Text)
|
||||
screen:print("KERNEL PANIC!\n"..msg.."\nSystem halted.")
|
||||
screen:print("Press any key to continue...")
|
||||
end
|
||||
while true do
|
||||
local event={computer:getMachineEvent()}
|
||||
if event[1]=="keyPressed" then
|
||||
break
|
||||
end
|
||||
end
|
||||
computer:reboot()
|
||||
end
|
||||
kernel.panic=kernel.PANIC
|
||||
|
||||
if windowsExp then
|
||||
screen:setTextColor(1)
|
||||
screen:setBackgroundColor(4)
|
||||
screen:clear()
|
||||
local w,h = screen:getSize()
|
||||
screen:setCursorPos(3,5)
|
||||
screen:print(":(")
|
||||
screen:setCursorPos(3,7)
|
||||
screen:print("Your PC ran into a problem and needs to restart. We're just collecting some error")
|
||||
screen:setCursorPos(3,8)
|
||||
screen:print("info, and then we'll restart for you.\n")
|
||||
screen:setCursorPos(3,h-5)
|
||||
screen:print("Stop code: average windows experience")
|
||||
screen:setCursorPos(1,h)
|
||||
screen:print("Press any key to continue... jk reboot it yourself lazy")
|
||||
while true do end
|
||||
end
|
||||
|
||||
kernel.log("Kernel loaded.")
|
||||
kernel.log("Mounting init disks...")
|
||||
disks.refresh()
|
||||
ifs.update(disks)
|
||||
kernel.disks={}
|
||||
for _,v in disks.list() do
|
||||
kernel.disks[v.address] = v
|
||||
end
|
||||
ifs.mount("$", "/")
|
||||
|
||||
local fstab=ifs.readAllText("/boot/fstab")
|
||||
local split = function(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
|
||||
|
||||
if not ifs.isFile("/boot/boot.cfg") then
|
||||
kernel.log("boot.cfg missing or corrupted!, Attempting to write recovery boot.cfg", "ERROR")
|
||||
ifs.writeAllText("/boot/boot.cfg",ifs.readAllText("/boot/safeboot.cfg"))
|
||||
end
|
||||
local initCfgFunc, err = load(ifs.readAllText("/boot/boot.cfg"), "@boot.cfg")
|
||||
if not initCfgFunc then
|
||||
kernel.PANIC("Failed to load /boot/boot.cfg: "..tostring(err))
|
||||
end
|
||||
|
||||
local initCfgStatus, config = pcall(initCfgFunc)
|
||||
if not initCfgStatus then
|
||||
kernel.PANIC("Error in /boot/boot.cfg: "..tostring(config))
|
||||
end
|
||||
kernel.config = config
|
||||
|
||||
for i,v in ipairs(split(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)
|
||||
ifs.mount(id,path)
|
||||
::endline::
|
||||
end
|
||||
end
|
||||
kernel.log("Disks initialized")
|
||||
|
||||
function kernel.saveLog()
|
||||
ifs.writeAllText("/var/log/syslog.log", LOG_Text)
|
||||
end
|
||||
|
||||
ifs.remove("/tmp")
|
||||
ifs.makeDir("/tmp")
|
||||
|
||||
function kernel.newFifo()
|
||||
local fifo = {}
|
||||
fifo.push=function(data)
|
||||
table.insert(data)
|
||||
end
|
||||
fifo.pop=function()
|
||||
return table.remove(fifo,1)
|
||||
end
|
||||
return fifo
|
||||
end
|
||||
|
||||
function kernel.newUUID()
|
||||
local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
||||
local uuid = ""
|
||||
for i = 1, #template do
|
||||
local c = template:sub(i,i)
|
||||
if c == "x" then
|
||||
uuid = uuid .. string.format("%x", math.random(0, 15))
|
||||
elseif c == "y" then
|
||||
uuid = uuid .. string.format("%x", math.random(8, 11))
|
||||
else
|
||||
uuid = uuid .. c
|
||||
end
|
||||
end
|
||||
return uuid
|
||||
end
|
||||
|
||||
kernel.syscalls={}
|
||||
local modules={[0]={}}
|
||||
for i=0, 100 do
|
||||
modules[i]={}
|
||||
end
|
||||
|
||||
kernel.log("Gathering modules")
|
||||
for _, i in ipairs(ifs.list("/lib/modules")) do
|
||||
for _,v in ipairs(ifs.list("/lib/modules/"..i)) do
|
||||
local prior=tonumber(v:sub(1,2))
|
||||
modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v
|
||||
end
|
||||
end
|
||||
|
||||
kernel.ifs=ifs
|
||||
kernel.apis=apis
|
||||
kernel.computer=computer
|
||||
kernel.arch=arch
|
||||
kernel.initdisks=disks
|
||||
kernel.screen=screen
|
||||
kernel.processes={}
|
||||
kernel.fstab=fstab
|
||||
|
||||
kernel.kernelTask = {
|
||||
name="kernel",
|
||||
status="R",
|
||||
pid=0,
|
||||
tgid=0,
|
||||
user="root",
|
||||
uid=0,
|
||||
fd={},
|
||||
exit="",
|
||||
sleep=0,
|
||||
ivs=0,
|
||||
vs=0,
|
||||
children={},
|
||||
syscallReturn={},
|
||||
cwd="/",
|
||||
timeSlice=0,
|
||||
lastTime=0,
|
||||
totalTime=0,
|
||||
numRuns=0
|
||||
}
|
||||
kernel.currentTask = kernel.kernelTask
|
||||
|
||||
kernel.syscalls["OS_time"]=function() return kernel.computer:time() end
|
||||
kernel.syscalls["OS_log"]=kernel.log
|
||||
|
||||
kernel.log("Running modules")
|
||||
for _,p in ipairs(modules) do
|
||||
for _,v in ipairs(p) do
|
||||
local code=ifs.readAllText(v)
|
||||
if not code then
|
||||
kernel.log("ModuReadErr: "..v, "WARN")
|
||||
goto skip
|
||||
end
|
||||
local func,err=load(code,"@"..v)
|
||||
if not func then kernel.panic("ModuLoadErr: "..tostring(err)) goto skip end
|
||||
local status, err = xpcall(func,debug.traceback, kernel)
|
||||
if not status then kernel.panic("ModuRunErr: "..tostring(err)) end
|
||||
::skip::
|
||||
end
|
||||
end
|
||||
|
||||
kernel.log("Kernel initialized successfully.")
|
||||
kernel.status="running"
|
||||
kernel.main()
|
||||
kernel.PANIC("Execution complete")
|
||||
11
Src/Hyperion-kernel-v1.0.0/boot/safeboot.cfg
Normal file
11
Src/Hyperion-kernel-v1.0.0/boot/safeboot.cfg
Normal file
@@ -0,0 +1,11 @@
|
||||
-- DO NOT EDIT THIS FILE IF YOU DO NOT KNOW WHAT YOU ARE DOING!
|
||||
-- DOING SO MAY RENDER YOUR SYSTEM UNBOOTABLE!
|
||||
|
||||
-- This file is auto-generated during the build process.
|
||||
-- RECOVERY BOOT CONFIGURATION FILE
|
||||
return {
|
||||
initPath = "/sbin/init.lua",
|
||||
maxOpenFiles = 128,
|
||||
maxFilesPerTask = 16,
|
||||
preempt=true
|
||||
}
|
||||
285
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/00_stdlib.kmod
Normal file
285
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/00_stdlib.kmod
Normal 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
|
||||
278
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/10_vfs.kmod
Normal file
278
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/10_vfs.kmod
Normal 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")
|
||||
@@ -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
|
||||
173
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/12_devfs.kmod
Normal file
173
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/12_devfs.kmod
Normal 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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
76
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/20_ipc.kmod
Normal file
76
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/20_ipc.kmod
Normal 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")
|
||||
@@ -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
|
||||
66
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/40_pam.kmod
Normal file
66
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/40_pam.kmod
Normal 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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
local debug=debug
|
||||
kernel._G.debug={
|
||||
getinfo=debug.getinfo,
|
||||
traceback=debug.traceback
|
||||
}
|
||||
126
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/50_tty.kmod
Normal file
126
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/50_tty.kmod
Normal 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"
|
||||
@@ -0,0 +1,3 @@
|
||||
--:Minify:--
|
||||
local kernel=...
|
||||
kernel.tty.bind("tty0")
|
||||
@@ -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
|
||||
45
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/99_init.kmod
Normal file
45
Src/Hyperion-kernel-v1.0.0/lib/modules/Hyperion/99_init.kmod
Normal 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...")
|
||||
Reference in New Issue
Block a user