mSL/SysFS is a kernel-extension implementation of the Linux /sys (sysfs) file
system for macOS, exposing the system's device model — buses, classes, devices
and their attributes — as a filesystem.
macOS Subsystem for Linux / SysFS — a native kernel-extension implementation
of /sys for macOS, presenting macOS's own device registry through the
Linux-compatible sysfs layout.
One module of mSL/XNU, a modular macOS Subsystem for Linux.
Status: early.
/sysmounts, and/sys/devicesnow mirrors the IOKit registry — every registry entry appears as a directory (recursively, keyed byIORegistryEntryID) with a readablenameattribute, walked in-kernel with no daemon. The other top-level directories (class/,bus/,block/,dev/,module/,kernel/, …) are still the empty skeleton, and per-device attributes beyondnameare not enumerated yet. See Feature status.
mSL/XNU — macOS Subsystem for Linux / X is Now UNIX — aims at native, seamless execution of Linux ELF binaries on macOS: not in a container and not in a virtual machine, but as ordinary processes on the running system.
Reaching that needs several independent pieces, which is why the project is modular rather than one monolith. Each is useful on its own, and each can be installed, replaced or omitted:
| Piece | What it does | Where |
|---|---|---|
| Filesystem Hierarchy Standard | The Linux filesystem layout, natively | mSL/FHS |
| Syscall translation | Linux system calls onto Darwin's, over Hypervisor.framework |
mSL/NABI |
| procfs | /proc, as a real filesystem |
mSL/ProcFS |
| sysfs | /sys, likewise |
this repository |
| devfs | /dev — already part of macOS |
XNU |
This repository is the SysFS piece, and it is a work in progress. The rest of this document describes it.
On Linux, sysfs is a virtual filesystem (mounted at /sys) that exports the
kernel's device model to userspace: the buses on the system, the device
classes, every device object, the drivers bound to them, and each object's
tunable/informational attributes — all as directories and small text files.
Almost everything in /sys is a view of one underlying tree of kobjects; the
class/, bus/, block/ and dev/ hierarchies are largely symbolic links into the
canonical /sys/devices tree.
macOS has no /sys, but it has the perfect analog for the device model itself:
the IOKit registry (the IORegistry). The IORegistry is a live tree of device
objects (IOService nodes) carrying typed properties, addressable by a stable
64-bit IORegistryEntryID — structurally the same idea as Linux's kobject tree.
SysFS is built on that mapping.
The core of SysFS (in the passes that follow this scaffold) is:
| sysfs path | macOS source |
|---|---|
devices/ |
the IORegistry itself — each registry entry becomes a directory, each IOKit property becomes an attribute file, keyed by IORegistryEntryID |
class/ |
device-class groupings (net, tty, power_supply, thermal, …), symlinks into devices/ |
bus/ |
per-bus devices/ + drivers/, from IOKit provider families (PCI, USB, …) |
block/ |
block devices (IOMedia), symlinks into devices/ |
dev/ |
char/ and block/ major:minor symlinks into devices/ |
module/ |
loaded kernel modules — macOS kexts (kextstat-equivalent) |
kernel/ |
kernel tunables and info, from the sysctl MIB |
firmware/ |
the device tree (firmware/devicetree), plus ACPI/DMI where present |
fs/ |
filesystem-specific tunables |
power/ |
system power-management state |
hypervisor/ |
hypervisor interface (empty when not running under one) |
IOKit registry traversal, entry ids and property reads are all public in-kernel
KPI (com.apple.kpi.iokit), so — unlike the procfs sibling, whose daemon exists
for task_for_pid/VM introspection that genuinely can't be done in-kernel —
SysFS walks the registry directly in the kext, with no sysfsd daemon. This
lives in one C++ translation unit (kext/sysfs_iokit.cpp) exposing a small
extern "C" surface to the C filesystem code; it builds against the plain
macOS SDK only (no MacKernelSDK — the C++ runtime symbols resolve at load
against com.apple.kpi.libkern). Anything unreachable degrades gracefully
(empty directory) rather than failing the mount.
The node model already reflects this: a sysfs node's identity
(struct sfsid, include/fs/sysfs/sysfs.h) is keyed on the backing
IORegistryEntryID, so the dynamic device tree drops in without reworking the
core.
Working:
- The kext loads and registers the
sysfsVFS type. mount_sysfsmounts/sysas a local, read-only filesystem.- Directory listing (
ls,find,readdir(3),getdirentries64(2)) of the root, returning the fixed Linux/systop-level directories:block bus class dev devices firmware fs hypervisor kernel module power(anddev/char,dev/block). stat(2)on every node (world-readable, root-owned: directories0555, files0444)./sys/devicesmirrors the IOKit registry: an in-kernel C++ IOKit translation unit walksgIOServicePlane, so each registry entry appears as a directory (recursively, keyed byIORegistryEntryID, named by its IOKit name with sibling-collision suffixes) with a readablenameattribute file.- Clean unmount and kext unload (no leaked vnodes).
Planned (not yet implemented):
- Full IOKit property → attribute enumeration (beyond
name). - The
class/,bus/,block/,dev/symlink views intodevices/. module/,kernel/,firmware/,fs/,power/content.- GUI / preference pane, installer package, and the test suite.
include/fs/sysfs/sysfs.h shared node model
include/fs/sysfs/sysfs_iokit.h C surface of the IOKit translation unit
kext/ the kernel extension
sysfs.c kmod start/stop, init/fini
sysfs_vfsops.c VFS ops: mount/unmount/root/getattr
sysfs_vnops.c vnode ops: lookup/readdir/getattr/read/reclaim
sysfs_node.c sfsnode hash table + find/create
sysfs_structure.c the /sys skeleton tree (+ the devices node)
sysfs_subr.c generic helpers (allocvp, fileid, sizes)
sysfs_iokit.cpp in-kernel IORegistry walk (the one C++ TU)
fs/ the mount bundle (sysfs.fs) + mount_sysfs
tools/ boot auto-mount: mount-sysfs + com.beako.sysfs.plist
lib/ vendored libraries (git submodules)
include/xnu/ vendored XNU private headers
Prerequisites: Xcode command-line tools, and the submodules + vendored headers checked out:
git submodule update --init --recursiveThen build the libraries, kext and mount bundle into out/:
make # native arch (arm64e on Apple Silicon)
make ARCH=x86_64 # IntelLoading a third-party kext requires the usual reduced-security posture (Recovery → Startup Security Utility → Reduced Security + Allow user management of kernel extensions), then approval in System Settings on first load. These steps need administrator rights.
sudo make -C kext load # load the kext
kextstat | grep sysfs # confirm it registered
mkdir -p /tmp/sys
sudo ./out/sysfs.fs/Contents/Resources/mount_sysfs sysfs /tmp/sys
ls /tmp/sys # block bus class dev devices firmware ...
ls /tmp/sys/devices # the IOKit registry mirror
sudo umount /tmp/sys
sudo make -C kext unloadsudo make install installs only the kext and sysfs.fs — it does not
touch boot. Boot auto-mount is a separate, opt-in step, because mounting /sys
at boot can hang login if the filesystem isn't yet safe to expose to the system's
volume scanners (see the note below):
sudo make install # kext + mount bundle only (never affects boot)To enable boot auto-mount, install the LaunchDaemon (com.beako.sysfs): a system
daemon that at boot runs /usr/local/sbin/mount-sysfs, which loads the kext and
mounts sysfs at /sys. Because /sys is on the read-only system volume, this
also adds sys to /etc/synthetic.conf so the mount point is created at boot.
Mounting /sys needs root, so it is a system LaunchDaemon, not a per-user login
agent (mirroring how procfs mounts /proc). It stays disarmed until you
create the arm flag (like procfs's /var/db/procfs.enabled), so a fault in the
kernel code cannot boot-loop the machine:
# Only after verifying the mount is safe while logged in (see below):
sudo make install-daemon # install the auto-mount LaunchDaemon
sudo touch /var/db/sysfs.enabled # arm it (one time)
sudo reboot # /sys is created, kext loads, sysfs mountsrm /var/db/sysfs.enabled disarms it again; sudo make uninstall removes the
daemon, the sys synthetic entry, unmounts /sys, and removes the kext/fs.
Why the caution:
/sys/devicesis a deep, live IORegistry tree. If the volume is browsable, Spotlight indexes it and the recursive walk can hangcoreservicesdand break login. The mount is markedMNT_DONTBROWSEto keep it out of the browse/index path (as devfs is), but verify a manual mount is safe —mount | grep sysfsshowsnobrowse, and locking/switching users and logging back in works — before arming boot auto-mount.
Built to the same standards as, and structurally derived from, the mSL/ProcFS kernel extension (itself descended from Kim Topley's macOS procfs). See mSL/ProcFS.
MIT — see LICENSE.