blobsets/genode/blobsets_genode/src/blobset_rom.nim

187 lines
5.8 KiB
Nim

import xmltree, streams, strtabs, strutils, xmlparser, tables, cbor,
genode, genode/parents, genode/servers, genode/roms,
dagfs, dagfs/stores, ./dagfs_client, dagfs/fsnodes
const
currentPath = currentSourcePath.rsplit("/", 1)[0]
header = currentPath & "/rom_component.h"
{.passC: "-I" & currentPath.}
type
RomSessionCapability {.
importcpp: "Genode::Rom_session_capability",
header: "<rom_session/capability.h>".} = object
RomSessionComponentBase {.importcpp, header: header.} = object
impl {.importcpp.}: pointer
RomSessionComponent = Constructible[RomSessionComponentBase]
Session = ref object
env: GenodeEnv
cpp: RomSessionComponent
ds: DataspaceCapability
proc isValid(cap: RomSessionCapability): bool {.importcpp: "#.valid()".}
proc deliverSession*(parent: Parent; id: ServerId; cap: RomSessionCapability) {.
importcpp: "#->deliver_session_cap(Genode::Parent::Server::Id{#}, #)".}
proc newSession(env: GenodeEnv; ds: DataspaceCapability): Session =
proc construct(cpp: RomSessionComponent; ds: DataspaceCapability) {.importcpp.}
new result
result.env = env
result.cpp.construct(ds)
result.ds = ds
proc manage(ep: Entrypoint; s: Session): RomSessionCapability =
proc manage(ep: Entrypoint; cpp: RomSessionComponent): RomSessionCapability {.
importcpp: "#.manage(*#)".}
result = ep.manage(s.cpp)
GC_ref s
proc dissolve(s: Session) =
proc dissolve(ep: Entrypoint; cpp: RomSessionComponent) {.
importcpp: "#.dissolve(*#)".}
let
ep = s.env.ep
pd = s.env.pd
ep.dissolve(s.cpp)
destruct s.cpp
pd.freeDataspace s.ds
GC_unref s
proc readFile(store: DagfsStore; s: Stream; file: FsNode) =
var chunk = ""
if file.isRaw:
store.get(file.cid, chunk)
assert(file.cid.verify chunk)
s.write chunk
else:
var n = 0
for i in 0..file.links.high:
store.get(file.links[i].cid, chunk)
assert(file.links[i].cid.verify chunk)
doAssert(n+chunk.len <= file.size)
s.write chunk
n.inc chunk.len
doAssert(n == file.size)
componentConstructHook = proc(env: GenodeEnv) =
var
store = env.newDagfsFrontend()
policies = newSeq[XmlNode](8)
sessions = initTable[ServerId, Session]()
proc readDataspace(label: string; rootCid: Cid): DataspaceCapability =
let
name = label.lastLabelElement
root = store.openDir(rootCid)
file = store.walk(root, name)
if file.isNil:
echo name, " not in root ", rootCid, " for '", label, "'"
else:
let
pd = env.pd
romDs = pd.allocDataspace file.size.int
dsFact = env.rm.newDataspaceStreamFactory(romDs)
romS = dsFact.newStream()
try: store.readFile(romS, file)
except:
close romS
pd.freeDataspace romDs
raise getCurrentException()
close romS
close dsFact
result = romDs
proc createSessionNoTry(id: ServerId; label: string; rootCid: Cid): RomSessionCapability =
let romDs = readDataspace(label, rootCid)
if romDs.isValid:
let session = env.newSession(romDs)
sessions[id] = session
result = env.ep.manage(session)
proc createSession(env: GenodeEnv; id: ServerId; label: string; rootCid: Cid) =
var cap = RomSessionCapability()
try: cap = createSessionNoTry(id, label, rootCid)
except MissingChunk:
let e = (MissingChunk)getCurrentException()
echo "cannot resolve '", label, "', ", e.cid, " is missing"
except:
echo "unhandled exception while resolving '", label, "', ",
getCurrentExceptionMsg()
discard
if cap.isValid:
echo "deliver ROM to ", label
env.parent.deliverSession id, cap
else:
echo "deny ROM to ", label
let parent = env.parent
parent.sessionResponseDeny(id)
proc processConfig(rom: RomClient) =
update rom
policies.setLen 0
let
configXml = rom.xml
configXml.findAll("default-policy", policies)
if policies.len > 1:
echo "more than one '<default-policy/>' found, ignoring all"
policies.setLen 0
configXml.findAll("policy", policies)
proc processSessions(rom: RomClient) {.gcsafe.} =
update rom
var requests = initSessionRequestsParser(rom)
for id in requests.close:
if sessions.contains id:
let s = sessions[id]
dissolve(s)
sessions.del id
env.parent.sessionResponseClose(id)
for id, label, args in requests.create "ROM":
try:
let policy = policies.lookupPolicy label
doAssert(not sessions.contains(id), "session already exists for id")
doAssert(label != "")
if policy.isNil:
echo "no policy matched '", label, "'"
env.parent.sessionResponseDeny(id)
else:
var rootCid = initCid()
let pAttrs = policy.attrs
if not pAttrs.isNil and pAttrs.contains "root":
try: rootCid = parseCid(pAttrs["root"])
except ValueError: discard
else:
for e in label.elements:
try:
rootCid = parseCid e
break
except ValueError: continue
if rootCid.isValid:
createSession(env, id, label, rootCid)
else:
echo "no valid root policy for '", label, "'"
env.parent.sessionResponseDeny(id)
except:
echo "failed to create session for '", label, "', ", getCurrentExceptionMsg()
env.parent.sessionResponseDeny(id)
# All sessions have been instantiated and requests fired off,
# now return to the entrypoint and dispatch packet signals,
# which will work the chain of futures and callbacks until
# `createSession` completes and capabilities are delivered
let
configRom = env.newRomHandler(
"config", processConfig)
sessionsRom = env.newRomHandler(
"session_requests", processSessions)
process configRom
process sessionsRom
env.parent.announce "ROM"