317 lines
10 KiB
Nim
317 lines
10 KiB
Nim
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later.txt
|
|
|
|
import std/[asyncdispatch, deques, options, sets, streams, tables, xmlparser, xmltree]
|
|
from std/sequtils import toSeq
|
|
import genode, genode/[entrypoints, parents, roms, servers, signals]
|
|
import preserves, preserves/xmlhooks
|
|
import syndicate, syndicate/[actors, relays]
|
|
import ./parent_facets
|
|
|
|
type
|
|
SessionLabel = seq[string]
|
|
|
|
Report {.preservesRecord: "report".} = object
|
|
label: SessionLabel
|
|
content: Assertion
|
|
|
|
SessionObj = object of RootObj
|
|
label: SessionLabel
|
|
dsCap: RamDataspaceCapability
|
|
streams: DataspaceStreamFactory
|
|
facet: Facet
|
|
dsRef: Ref
|
|
|
|
ReportSessionCapability {.
|
|
importcpp: "Genode::Capability<Report::Session>".} = object
|
|
|
|
ReportSessionComponent = Constructible[ReportSessionComponentBase]
|
|
ReportSessionComponentBase {.
|
|
importcpp: "Report::Session_component",
|
|
header: "report_service.cpp".} = object
|
|
|
|
ReportSession = ref ReportSessionObj
|
|
ReportSessionPtr = ptr ReportSessionObj
|
|
ReportSessionObj = object of SessionObj
|
|
component: ReportSessionComponent
|
|
reportHandle: Handle
|
|
|
|
RomSessionCapability {.
|
|
importcpp: "Genode::Rom_session_capability",
|
|
header: "<rom_session/capability.h>".} = object
|
|
|
|
RomSessionComponent = Constructible[RomSessionComponentBase]
|
|
RomSessionComponentBase {.
|
|
importcpp: "Rom::Session_component",
|
|
header: "rom_service.cpp".} = object
|
|
|
|
RomSession = ref RomSessionObj
|
|
RomSessionPtr = ptr RomSessionObj
|
|
RomSessionObj = object of SessionObj
|
|
component: RomSessionComponent
|
|
signalContext: SignalContextCapability
|
|
|
|
TerminalSessionCapability {.
|
|
importcpp: "Genode::Capability<Terminal::Session>".} = object
|
|
|
|
TerminalSessionComponent = Constructible[TerminalSessionComponentBase]
|
|
TerminalSessionComponentBase {.
|
|
importcpp: "Terminal::Session_component",
|
|
header: "terminal_service.cpp".} = object
|
|
|
|
TerminalSession = ref TerminalSessionObj
|
|
TerminalSessionPtr = ptr TerminalSessionObj
|
|
TerminalSessionObj = object of SessionObj
|
|
component: TerminalSessionComponent
|
|
relay: Relay
|
|
receiveBuffer: BufferedDecoder
|
|
pendingPackets: Deque[Packet]
|
|
readAvailSignal: SignalContextCapability
|
|
|
|
SessionComponent =
|
|
ReportSessionComponent |
|
|
RomSessionComponent |
|
|
TerminalSessionComponent
|
|
Session =
|
|
ReportSession |
|
|
RomSession |
|
|
TerminalSession
|
|
|
|
proc construct(
|
|
cpp: ReportSessionComponent;
|
|
state: pointer;
|
|
ds: RamDataspaceCapability) {.importcpp.}
|
|
|
|
proc construct(
|
|
cpp: RomSessionComponent;
|
|
state: pointer;
|
|
ds: RamDataspaceCapability) {.importcpp.}
|
|
|
|
proc construct(
|
|
cpp: TerminalSessionComponent;
|
|
state: pointer;
|
|
ds: RamDataspaceCapability) {.importcpp.}
|
|
|
|
proc dissolve(ep: Entrypoint; s: SessionComponent) {.importcpp: "#.dissolve(*#)".}
|
|
|
|
proc dissolve(env: GenodeEnv; s: Session) =
|
|
close(s.streams)
|
|
dissolve(env.ep, s.component)
|
|
freeDataspace(env.pd, s.dsCap)
|
|
destruct(s.component)
|
|
GC_unref s
|
|
|
|
proc dissolve(env: GenodeEnv; turn: var Turn; session: ReportSession) =
|
|
retract(turn, session.reportHandle)
|
|
dissolve(env, session)
|
|
|
|
proc dissolve(env: GenodeEnv; turn: var Turn; session: RomSession) =
|
|
dissolve(env, session)
|
|
|
|
proc dissolve(env: GenodeEnv; turn: var Turn; session: TerminalSession) =
|
|
stop(turn, session.facet)
|
|
dissolve(env, session)
|
|
|
|
proc manage(ep: Entrypoint; s: ReportSession): ReportSessionCapability =
|
|
proc manage(ep: Entrypoint; cpp: ReportSessionComponent
|
|
): ReportSessionCapability {.importcpp: "#.manage(*#)".}
|
|
result = manage(ep, s.component)
|
|
GC_ref s
|
|
|
|
proc manage(ep: Entrypoint; s: RomSession): RomSessionCapability =
|
|
proc manage(ep: Entrypoint; cpp: RomSessionComponent
|
|
): RomSessionCapability {.importcpp: "#.manage(*#)".}
|
|
result = manage(ep, s.component)
|
|
GC_ref s
|
|
|
|
proc manage(ep: Entrypoint; s: TerminalSession): TerminalSessionCapability =
|
|
proc manage(ep: Entrypoint; cpp: TerminalSessionComponent
|
|
): TerminalSessionCapability {.importcpp: "#.manage(*#)".}
|
|
result = manage(ep, s.component)
|
|
GC_ref s
|
|
|
|
proc initSession(
|
|
ses: var SessionObj;
|
|
env: GenodeEnv;
|
|
label: SessionLabel;
|
|
dsCap: RamDataspaceCapability;
|
|
facet: Facet;
|
|
dsRef: Ref) =
|
|
ses.label = label
|
|
ses.dsCap = dsCap
|
|
ses.streams = newDataspaceStreamFactory(env.rm, ses.dsCap)
|
|
ses.facet = facet
|
|
ses.dsRef = dsRef
|
|
|
|
proc newReportSession(
|
|
env: GenodeEnv;
|
|
ds: Ref; turn: var Turn;
|
|
label: SessionLabel; size: int
|
|
): ReportSession =
|
|
var dsCap = allocDataspace(env.pd, size)
|
|
new result
|
|
initSession(result[], env, label, dsCap, turn.facet, ds)
|
|
construct(result.component, addr(result[]), dsCap)
|
|
|
|
proc newRomSession(
|
|
env: GenodeEnv;
|
|
ds: Ref; turn: var Turn;
|
|
label: SessionLabel; size: int
|
|
): RomSession =
|
|
var dsCap = allocDataspace(env.pd, size)
|
|
new result
|
|
initSession(result[], env, label, dsCap, turn.facet, ds)
|
|
construct(result.component, addr(result[]), dsCap)
|
|
|
|
proc newTerminalSession(
|
|
env: GenodeEnv;
|
|
ds: Ref; turn: var Turn;
|
|
label: SessionLabel; size: int
|
|
): TerminalSession =
|
|
var dsCap = allocDataspace(env.pd, size)
|
|
result = TerminalSession(receiveBuffer: newBufferedDecoder())
|
|
initSession(result[], env, label, dsCap, turn.facet, ds)
|
|
construct(result.component, addr(result[]), dsCap)
|
|
let session = result
|
|
proc queuePacket(pkt: sink Packet): Future[void] {.async.} =
|
|
session.pendingPackets.addLast(pkt)
|
|
session.readAvailSignal.submit()
|
|
# notify the client of the pending packet
|
|
var opts = RelayActorOptions(
|
|
packetWriter: queuePacket,
|
|
initialRef: session.dsRef,
|
|
initialOid: 0.Oid.some)
|
|
run(session.facet) do (turn: var Turn):
|
|
asyncCheck spawnRelay($session.label, turn, opts) do (turn: var Turn; relay: Relay):
|
|
session.facet = turn.facet
|
|
session.relay = relay
|
|
|
|
type SessionCapability =
|
|
ReportSessionCapability |
|
|
RomSessionCapability |
|
|
TerminalSessionCapability
|
|
|
|
proc deliverSession*(parent: Parent; id: ServerId; cap: SessionCapability) {.
|
|
importcpp: "#->deliver_session_cap(Genode::Parent::Server::Id{#}, #)".}
|
|
|
|
proc splitLabel(joined: string): SessionLabel =
|
|
joined.labelElements.toSeq
|
|
|
|
proc syndicate_report_submit(state: pointer; length: csize_t) {.exportc.} =
|
|
assert not state.isNil
|
|
let session = cast[ReportSessionPtr](state)
|
|
run(session.facet) do (turn: var Turn):
|
|
var stream = newStream(session.streams, int length)
|
|
try:
|
|
replace(turn, session.dsRef, session.reportHandle, Report(
|
|
label: session.label, content: parseXml(stream).toPreserve(Ref)))
|
|
except:
|
|
var raw = newSeq[byte](stream.size)
|
|
copyMem(addr raw[0], session.streams.data, raw.len)
|
|
replace(turn, session.dsRef, session.reportHandle, Report(
|
|
label: session.label, content: raw.toPreserve(Ref)))
|
|
close(stream)
|
|
scheduleCallbacks()
|
|
|
|
proc syndicate_rom_update(state: pointer) {.exportc.} =
|
|
assert not state.isNil
|
|
let session = cast[RomSessionPtr](state)
|
|
run(session.facet) do (turn: var Turn):
|
|
var stream = newStream(session.streams)
|
|
write(stream, "syndicate_rom_update not implemented, sorry")
|
|
close(stream)
|
|
scheduleCallbacks()
|
|
|
|
proc syndicate_rom_sigh(state: pointer; cap: SignalContextCapability) {.exportc.} =
|
|
assert not state.isNil
|
|
let session = cast[RomSessionPtr](state)
|
|
session.signalContext = cap
|
|
|
|
proc syndicate_terminal_read_avail_sigh(
|
|
state: pointer; cap: SignalContextCapability) {.exportc.} =
|
|
let session = cast[TerminalSessionPtr](state)
|
|
session.readAvailSignal = cap
|
|
if session.pendingPackets.len > 0:
|
|
session.readAvailSignal.submit()
|
|
|
|
proc syndicate_terminal_avail(state: pointer): bool {.exportc.} =
|
|
let sesssion = cast[TerminalSessionPtr](state)
|
|
sesssion.pendingPackets.len > 0
|
|
|
|
proc syndicate_terminal_write(state: pointer; length: csize_t): csize_t {.exportc.} =
|
|
let sesssion = cast[TerminalSessionPtr](state)
|
|
var stream = sesssion.streams.newStream(int length)
|
|
if sesssion.pendingPackets.len > 0:
|
|
write(stream, sesssion.pendingPackets.popFirst().toPreserve(WireRef))
|
|
result = stream.getPosition.csize_t
|
|
close(stream)
|
|
if sesssion.pendingPackets.len > 0:
|
|
sesssion.readAvailSignal.submit()
|
|
scheduleCallbacks()
|
|
|
|
proc syndicate_terminal_read(state: pointer; length: csize_t): csize_t {.exportc.} =
|
|
let
|
|
session = cast[TerminalSessionPtr](state)
|
|
size = min(int length, session.streams.size)
|
|
session.receiveBuffer.feed(session.streams.data, size)
|
|
var (success, pr) = decode(session.receiveBuffer, WireRef)
|
|
if success:
|
|
dispatch(session.relay, pr)
|
|
scheduleCallbacks()
|
|
length
|
|
|
|
componentConstructHook = proc(env: GenodeEnv) =
|
|
bootDataspace("main") do (ds: Ref; turn: var Turn):
|
|
during(turn, ds, grab()) do (a: Assertion):
|
|
echo "publish: ", a
|
|
do:
|
|
echo "retract: ", a
|
|
|
|
# newConfigFacet(env, ds, turn)
|
|
newSessionRequestsFacet(env, ds, turn)
|
|
var
|
|
reports = initTable[ServerId, ReportSession]()
|
|
roms = initTable[ServerId, RomSession]()
|
|
terminals = initTable[ServerId, TerminalSession]()
|
|
|
|
onPublish(turn, ds, CreateRequest ? { 0: grab(), 1: ?Args }) do (
|
|
attrs: CreateAttrs, args: Assertion):
|
|
var
|
|
label = splitLabel(attrs.label)
|
|
id = ServerId attrs.id
|
|
case attrs.service
|
|
of "Report":
|
|
var
|
|
size = getOrDefault(args, "buffer_size", 0x1000)
|
|
session = newReportSession(env, ds, turn, label, size)
|
|
deliverSession(env.parent, id, manage(env.ep, session))
|
|
reports[id] = session
|
|
of "Rom":
|
|
const size = 0x1000
|
|
var session = newRomSession(env, ds, turn, label, size)
|
|
deliverSession(env.parent, id, manage(env.ep, session))
|
|
roms[id] = session
|
|
of "Terminal":
|
|
var
|
|
size = getOrDefault(args, "buffer_size", 0x1000)
|
|
session = newTerminalSession(env, ds, turn, label, size)
|
|
deliverSession(env.parent, id, manage(env.ep, session))
|
|
terminals[id] = session
|
|
else:
|
|
env.parent.sessionResponseDeny(id)
|
|
|
|
onPublish(turn, ds, ?CloseRequest) do (id: ServerId):
|
|
var
|
|
report: ReportSession
|
|
terminal: TerminalSession
|
|
if pop(reports, id, report):
|
|
dissolve(env, turn, report)
|
|
elif pop(terminals, id, terminal):
|
|
dissolve(env, turn, terminal)
|
|
sessionResponseClose(env.parent, id)
|
|
|
|
announce(env.parent, "Report")
|
|
announce(env.parent, "ROM")
|
|
announce(env.parent, "Terminal")
|
|
scheduleCallbacks()
|