Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions pyplugins/apis/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ def on_open(cpu, filename, flags):
"""

from penguin import plugins, Plugin
from penguin.plugin_manager import resolve_bound_method_from_class
from hyper.consts import igloo_hypercall_constants as iconsts
from typing import Iterator


EVENTS = {
Expand Down Expand Up @@ -103,6 +105,7 @@ def __init__(self):
"""
# MAGIC -> [fn1, fn2, fn3,...]
self.callbacks = {}
self._portalcall_handlers = set()

for event_num, (name, args) in EVENTS.items():
plugins.register(self, name, register_notify=self.register_notify)
Expand Down Expand Up @@ -148,6 +151,44 @@ def generic_hypercall(cpu):
raise ValueError(f"Unknown argument type {arg}")
plugins.publish(self, self.callbacks[magic], *args)

def _setup_portalcall_handler(self, magic, arg_types):
if magic in self._portalcall_handlers:
return
self._portalcall_handlers.add(magic)

@plugins.portalcall.portalcall(magic)
def generic_portalcall(*raw_args):
args = [None]
for i, arg in enumerate(arg_types):
argval = raw_args[i] if i < len(raw_args) else 0
if arg is int:
args.append(argval)
elif arg is str:
try:
args.append((yield from plugins.mem.read_str(argval)))
except ValueError:
self.logger.debug(
f"arg read fail: {magic} {argval:x} {i} {arg}"
)
return 1
elif arg is bool:
args.append(argval != 0)
elif arg is None:
pass
else:
raise ValueError(f"Unknown argument type {arg}")

result = 0
for cb in plugins.plugin_cbs[self][self.callbacks[magic]]:
if not hasattr(cb, '__self__') and hasattr(cb, '__qualname__') and '.' in cb.__qualname__:
cb = resolve_bound_method_from_class(cb)
cb_result = cb(*args)
if isinstance(cb_result, Iterator):
cb_result = yield from cb_result
if isinstance(cb_result, int):
result = cb_result
return result

def register_notify(self, name, callback):
"""
Register a callback for an event.
Expand All @@ -166,6 +207,7 @@ def register_notify(self, name, callback):
if ename == name:
if self.callbacks.get(magic, None) is None:
self._setup_hypercall_handler(magic, arg_types)
self._setup_portalcall_handler(magic, arg_types)
self.callbacks[magic] = []
self.callbacks[magic] = name
return
Expand Down
6 changes: 6 additions & 0 deletions pyplugins/apis/portalcall.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def my_portalcall_handler(arg1, arg2):

PORTAL_MAGIC = 0xc1d1e1f1
PORTAL_MAGIC_64 = 0xffffffffc1d1e1f1
PORTAL_MAGIC_MASK = 0xffffffff


class PortalCall(Plugin):
Expand All @@ -53,13 +54,18 @@ def __init__(self) -> None:
plugins.syscalls.syscall("on_sys_sendto_enter", arg_filters=[PORTAL_MAGIC, None, None, None, None])(self._portalcall_syscall_handler)

def _portalcall_syscall_handler(self, regs, proto, syscall, magic, user_magic, argc, args, dest_addr, addrlen):
if not self._is_portal_magic(magic):
return
result = yield from self._dispatch_portalcall(user_magic, argc, args)
syscall.skip_syscall = True
if isinstance(result, int):
syscall.retval = result
else:
syscall.retval = 0 # Default to 0 if result is not an int

def _is_portal_magic(self, magic: int) -> bool:
return int(magic) & PORTAL_MAGIC_MASK == PORTAL_MAGIC

def _dispatch_portalcall(self, user_magic, argc, args):
handler = self._portalcall_registry.get(user_magic & 0xffffffff)
if handler is None:
Expand Down
26 changes: 26 additions & 0 deletions pyplugins/compat/qemu_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
uint64_t *ret);
typedef int (*kvm_penguin_after_guest_init_cb_t)(MachineState *machine,
void *opaque);
typedef uint64_t (*penguin_mmio_read_cb_t)(uint64_t addr, unsigned size,
void *opaque);
typedef void (*penguin_mmio_write_cb_t)(uint64_t addr, uint64_t data,
unsigned size, void *opaque);

extern MachineState *current_machine;
extern int (*qemu_main)(void);
Expand Down Expand Up @@ -61,6 +65,11 @@
uint64_t a2, uint64_t a3,
uint64_t a4, uint64_t a5,
uint64_t *ret);
int penguin_qemu_add_mmio_region(uint64_t base, uint64_t size,
const char *name,
penguin_mmio_read_cb_t read_cb,
penguin_mmio_write_cb_t write_cb,
void *opaque);
"""


Expand Down Expand Up @@ -359,6 +368,23 @@ def __init__(
):
if declaration not in cdef_source:
cdef_source += f"\n{declaration}\n"
if "penguin_mmio_read_cb_t" not in cdef_source:
cdef_source += (
"\ntypedef uint64_t (*penguin_mmio_read_cb_t)"
"(uint64_t addr, unsigned size, void *opaque);\n"
)
if "penguin_mmio_write_cb_t" not in cdef_source:
cdef_source += (
"\ntypedef void (*penguin_mmio_write_cb_t)"
"(uint64_t addr, uint64_t data, unsigned size, void *opaque);\n"
)
if "penguin_qemu_add_mmio_region" not in cdef_source:
cdef_source += (
"\nint penguin_qemu_add_mmio_region("
"uint64_t base, uint64_t size, const char *name, "
"penguin_mmio_read_cb_t read_cb, "
"penguin_mmio_write_cb_t write_cb, void *opaque);\n"
)
self.ffi.cdef(cdef_source)

flags = getattr(os, "RTLD_GLOBAL", 0) | getattr(os, "RTLD_NOW", 0)
Expand Down
19 changes: 18 additions & 1 deletion pyplugins/hyperfile/anonfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,15 @@ def register_anon_file(self, vfs_file: VFSFile, name: str = "[igloo_anon]") -> G

fops = yield from self._make_fops_struct(vfs_file)
kffi = plugins.kffi
mmap_phys_addr = self._mmap_phys_addr(vfs_file)

init_data = {
"name": name.encode("latin-1", errors="ignore"),
"hf_id": hf_id,
"ops": fops
"ops": fops,
}
if mmap_phys_addr:
init_data["mmap_phys_addr"] = mmap_phys_addr

req = kffi.new("struct portal_anonfs_create_req", init_data)
req_bytes = bytes(req)
Expand All @@ -111,6 +114,20 @@ def register_anon_file(self, vfs_file: VFSFile, name: str = "[igloo_anon]") -> G
self.logger.debug(f"Injected anon file '{name}' at FD {fd}")
return fd

def _mmap_phys_addr(self, vfs_file: VFSFile) -> int:
supports_default_mmap = any(
(
getattr(vfs_file, "SUPPORT_MMAP", False),
getattr(vfs_file, "SIZE", 0),
)
) and not vfs_file._is_overridden("mmap")
if not supports_default_mmap:
return 0
if "qemu_mem" not in plugins:
return 0
qemu_mem = plugins.get_plugin_by_name("qemu_mem")
return qemu_mem.allocate_file(vfs_file)

def register_socket(self, sock_file: SocketFile) -> Generator[int, None, int]:
"""
Injects a true kernel socket object into the guest process table.
Expand Down
27 changes: 26 additions & 1 deletion pyplugins/hyperfile/devfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ def _register_devfs(self, devfs_list: List[Tuple[str, DevFile, int, int]]) -> Ge

ops = yield from self._make_ops_struct(devfs_file)
kffi = plugins.kffi
mmap_phys_addr = self._mmap_phys_addr(devfs_file)
support_mmap = any(
(
getattr(devfs_file, "SUPPORT_MMAP", False),
mmap_phys_addr,
)
)

init_data = {
"name": file_name.encode("latin-1", errors="ignore"),
Expand All @@ -194,10 +201,12 @@ def _register_devfs(self, devfs_list: List[Tuple[str, DevFile, int, int]]) -> Ge
"parent_id": parent_id,
"size": getattr(devfs_file, "SIZE", 0),
"mode": getattr(devfs_file, "MODE", 0o666),
"support_mmap": 1 if getattr(devfs_file, "SUPPORT_MMAP", False) else 0,
"support_mmap": 1 if support_mmap else 0,
"is_block": 1 if getattr(devfs_file, "IS_BLOCK", False) else 0,
"logical_block_size": getattr(devfs_file, "LOGICAL_BLOCK_SIZE", 512)
}
if mmap_phys_addr:
init_data["mmap_phys_addr"] = mmap_phys_addr

req = kffi.new("struct portal_devfs_create_req", init_data)
req_bytes = bytes(req)
Expand All @@ -216,6 +225,22 @@ def _register_devfs(self, devfs_list: List[Tuple[str, DevFile, int, int]]) -> Ge

self.logger.debug(f"Registered devfs device '{fname}' with kernel")

def _mmap_phys_addr(self, devfs_file: DevFile) -> int:
if not self._supports_default_mmap(devfs_file):
return 0
if "qemu_mem" not in plugins:
return 0
qemu_mem = plugins.get_plugin_by_name("qemu_mem")
return qemu_mem.allocate_file(devfs_file)

def _supports_default_mmap(self, devfs_file: DevFile) -> bool:
return any(
(
getattr(devfs_file, "SUPPORT_MMAP", False),
getattr(devfs_file, "SIZE", 0),
)
) and not devfs_file._is_overridden("mmap")

def _hyperdevfs_interrupt_handler(self) -> Generator[bool, None, bool]:
if not self._pending_devfs:
return False
Expand Down
27 changes: 26 additions & 1 deletion pyplugins/hyperfile/procfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,13 @@ def _register_procs(self, procs: List[Tuple[str, ProcFile]]) -> Generator[int, N

fops = yield from self._make_fops_struct(proc)
kffi = plugins.kffi
mmap_phys_addr = self._mmap_phys_addr(proc)
support_mmap = any(
(
getattr(proc, "SUPPORT_MMAP", False),
mmap_phys_addr,
)
)

init_data = {
"path": file_name.encode("latin-1", errors="ignore"),
Expand All @@ -188,8 +195,10 @@ def _register_procs(self, procs: List[Tuple[str, ProcFile]]) -> Generator[int, N
"mode": getattr(proc, "MODE", 0o444),
"parent_id": parent_id,
"replace": 1,
"support_mmap": 1 if getattr(proc, "SUPPORT_MMAP", False) else 0
"support_mmap": 1 if support_mmap else 0
}
if mmap_phys_addr:
init_data["mmap_phys_addr"] = mmap_phys_addr

req = kffi.new("struct portal_procfs_create_req", init_data)
req_bytes = bytes(req)
Expand All @@ -206,6 +215,22 @@ def _register_procs(self, procs: List[Tuple[str, ProcFile]]) -> Generator[int, N
continue
self.logger.debug(f"Registered proc '{fname}' with kernel")

def _mmap_phys_addr(self, proc: ProcFile) -> int:
if not self._supports_default_mmap(proc):
return 0
if "qemu_mem" not in plugins:
return 0
qemu_mem = plugins.get_plugin_by_name("qemu_mem")
return qemu_mem.allocate_file(proc)

def _supports_default_mmap(self, proc: ProcFile) -> bool:
return any(
(
getattr(proc, "SUPPORT_MMAP", False),
getattr(proc, "SIZE", 0),
)
) and not proc._is_overridden("mmap")

def _proc_interrupt_handler(self) -> Generator[bool, None, bool]:
"""
Process pending proc registrations.
Expand Down
Loading
Loading