The Pedigree Project 0.1
SysFs.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS 1
3
4#include "SysFs.h"
5#include "pedigree/kernel/BootstrapInfo.h"
6#include "pedigree/kernel/machine/Device.h"
7#include "pedigree/kernel/machine/Disk.h"
8#include "pedigree/kernel/utilities/utility.h"
9
10#include "DevFs-block.h"
13#include "modules/system/network-stack/NetworkStack.h"
14#include "modules/system/vfs/Symlink.h"
15#include "modules/system/vfs/VFS.h"
16
17namespace {
18class AttributeFile final : public File {
19 public:
20 AttributeFile(const String& name, uintptr_t inode, Filesystem* filesystem, File* parent,
21 const String& contents)
22 : File(name, 0, 0, 0, inode, filesystem, contents.length(), parent), m_Contents(contents) {
23 setPermissionsOnly(FILE_UR | FILE_GR | FILE_OR);
24 setUidOnly(0);
25 setGidOnly(0);
26 }
27
28 uint64_t readBytewise(uint64_t offset, uint64_t size, uintptr_t buffer, bool) override {
29 if (offset >= m_Contents.length())
30 return 0;
31 size = min(size, m_Contents.length() - offset);
32 MemoryCopy(reinterpret_cast<void*>(buffer), m_Contents.cstr() + offset, size);
33 return size;
34 }
35 uint64_t writeBytewise(uint64_t, uint64_t, uintptr_t, bool) override {
36 return 0;
37 }
38 size_t getSize() override {
39 return m_Contents.length();
40 }
41
42 private:
43 bool isBytewise() const override {
44 return true;
45 }
46 String m_Contents;
47};
48
49class SysFsLink final : public Symlink {
50 public:
51 SysFsLink(const String& name, uintptr_t inode, Filesystem* filesystem, File* parent,
52 const String& target)
53 : Symlink(name, 0, 0, 0, inode, filesystem, target.length(), parent) {
54 m_sTarget = target;
55 setPermissions(FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR | FILE_OW |
56 FILE_OX);
57 }
58};
59
60struct NetworkSnapshot {
61 struct netif* interface = nullptr;
62 char name[2] = {};
63 unsigned number = 0;
64 unsigned mtu = 0;
65 bool up = false;
66 bool link = false;
67 unsigned macLength = 0;
68 unsigned char mac[NETIF_MAX_HWADDR_LEN] = {};
69};
70
71void snapshotNetwork(void* context) {
72 auto& snapshot = *static_cast<NetworkSnapshot*>(context);
73 auto* interface = snapshot.interface;
74 MemoryCopy(snapshot.name, interface->name, sizeof(snapshot.name));
75 snapshot.number = interface->num;
76 snapshot.mtu = interface->mtu;
77 snapshot.up = netif_is_up(interface);
78 snapshot.link = netif_is_link_up(interface);
79 snapshot.macLength = min(static_cast<unsigned>(interface->hwaddr_len),
80 static_cast<unsigned>(sizeof(snapshot.mac)));
81 MemoryCopy(snapshot.mac, interface->hwaddr, snapshot.macLength);
82}
83} // namespace
84
85SysFs::~SysFs() {
86 if (!m_Root)
87 return;
88 m_Root->emptyCache();
89 if (!VFS::instance().untrackFile(m_Root))
90 ERROR("SysFs: root directory did not get cleaned up");
91}
92
94 static String label("sysfs");
95 return label;
96}
97
98SysFsDirectory* SysFs::directory(SysFsDirectory* parent, const char* name) {
99 auto* result = new SysFsDirectory(String(name), allocateInode(), this, parent);
100 if (!result)
101 return nullptr;
102 parent->addEntry(result->getName(), result);
103 return result;
104}
105
106void SysFs::attribute(SysFsDirectory* parent, const char* name, const String& contents) {
107 auto* file = new AttributeFile(String(name), allocateInode(), this, parent, contents);
108 if (file)
109 parent->addEntry(file->getName(), file);
110}
111
112void SysFs::symlink(SysFsDirectory* parent, const char* name, const String& target) {
113 auto* link = new SysFsLink(String(name), allocateInode(), this, parent, target);
114 if (link) {
115 parent->addEntry(link->getName(), link);
116 }
117}
118
119void SysFs::addPciDevices(SysFsDirectory* devices) {
120 auto add = [this, devices](Device* device) -> Device* {
121 const uint16_t vendor = device->getPciVendorId();
122 if (!vendor || vendor == 0xffff)
123 return device;
124 String name;
125 name.Format("0000:%02x:%02x.%x", device->getPciBusPosition(), device->getPciDevicePosition(),
126 device->getPciFunctionNumber());
127 auto* entry = directory(devices, name.cstr());
128 if (!entry)
129 return device;
130 String value;
131 value.Format("0x%04x\n", vendor);
132 attribute(entry, "vendor", value);
133 value.Format("0x%04x\n", device->getPciDeviceId());
134 attribute(entry, "device", value);
135 value.Format("0x%02x%02x%02x\n", device->getPciClassCode(), device->getPciSubclassCode(),
136 device->getPciProgInterface());
137 attribute(entry, "class", value);
138 value.Format("%lu\n", device->getInterruptNumber());
139 attribute(entry, "irq", value);
140 String resources;
141 for (auto* address : device->addresses()) {
142 const uintptr_t end =
143 address->m_Size ? address->m_Address + address->m_Size - 1 : address->m_Address;
144 String line;
145 line.Format("0x%016lx 0x%016lx 0x%016lx\n", address->m_Address, end,
146 address->m_IsIoSpace ? 1UL : 0UL);
147 resources += line;
148 }
149 attribute(entry, "resource", resources);
150 return device;
151 };
152 auto callback = pedigree_std::make_callable(add);
153 Device::foreach (callback, nullptr);
154}
155
156void SysFs::addBlockDevices(SysFsDirectory* devices) {
157 auto add = [this, devices](Device* device) -> Device* {
158 if (device->getType() != Device::Disk)
159 return device;
160 auto* disk = static_cast<Disk*>(device);
161 const uint32_t id = disk->endpointId();
162 if (!id)
163 return device;
164 String name;
165 name.Format("disk%u", id);
166 auto* entry = directory(devices, name.cstr());
167 if (!entry)
168 return device;
169 String value;
170 value.Format("%u:%u\n", PosixBlock::PhysicalMajor, id);
171 attribute(entry, "dev", value);
172 value.Format("%lu\n", disk->getSize() / 512);
173 attribute(entry, "size", value);
174 attribute(entry, "removable", String("0\n"));
175 return device;
176 };
177 auto callback = pedigree_std::make_callable(add);
178 Device::foreach (callback, nullptr);
179}
180
181void SysFs::addNetworkDevices(SysFsDirectory* devices) {
182 auto* stack = NetworkStack::instanceIfAvailable();
183 if (!stack)
184 return;
185 for (size_t index = 0;; ++index) {
187 if (!stack->acquireDevice(index, device))
188 break;
189 NetworkSnapshot snapshot;
190 snapshot.interface = device.interface();
191 if (tcpip_callback_wait(snapshotNetwork, &snapshot) != ERR_OK)
192 continue;
193 String name;
194 name.Format("%c%c%u", snapshot.name[0], snapshot.name[1], snapshot.number);
195 auto* entry = directory(devices, name.cstr());
196 if (!entry)
197 continue;
198 String value;
199 for (unsigned i = 0; i < snapshot.macLength; ++i) {
200 String octet;
201 octet.Format("%s%02x", i ? ":" : "", snapshot.mac[i]);
202 value += octet;
203 }
204 value += "\n";
205 attribute(entry, "address", value);
206 value.Format("%u\n", snapshot.mtu);
207 attribute(entry, "mtu", value);
208 attribute(entry, "operstate", String(snapshot.up && snapshot.link ? "up\n" : "down\n"));
209 }
210}
211
213 m_NextInode = 0;
214 delete m_Root;
215 m_Root = new SysFsDirectory(String(""), allocateInode(), this, nullptr);
216 if (!m_Root)
217 return false;
218 VFS::instance().trackFile(m_Root);
219
220 auto* classDirectory = directory(m_Root, "class");
221 auto* block = classDirectory ? directory(classDirectory, "block") : nullptr;
222 auto* network = classDirectory ? directory(classDirectory, "net") : nullptr;
223 auto* input = classDirectory ? directory(classDirectory, "input") : nullptr;
224 auto* graphics = classDirectory ? directory(classDirectory, "graphics") : nullptr;
225 auto* framebuffer = graphics ? directory(graphics, "fb0") : nullptr;
226 auto* framebufferDevice = framebuffer ? directory(framebuffer, "device") : nullptr;
227 auto* bus = directory(m_Root, "bus");
228 auto* pci = bus ? directory(bus, "pci") : nullptr;
229 auto* pciDevices = pci ? directory(pci, "devices") : nullptr;
230 auto* platform = bus ? directory(bus, "platform") : nullptr;
231 auto* firmware = directory(m_Root, "firmware");
232 if (!block || !network || !input || !framebufferDevice || !pciDevices || !platform || !firmware) {
233 return false;
234 }
235
236 auto* keyboard = directory(input, "event0");
237 auto* keyboardDevice = keyboard ? directory(keyboard, "device") : nullptr;
238 auto* pointer = directory(input, "event1");
239 auto* pointerDevice = pointer ? directory(pointer, "device") : nullptr;
240 if (!keyboardDevice || !pointerDevice) {
241 return false;
242 }
243 attribute(keyboard, "dev", String("13:64\n"));
244 attribute(keyboardDevice, "name", String("Pedigree keyboard\n"));
245 attribute(pointer, "dev", String("13:65\n"));
246 attribute(pointerDevice, "name", String("Pedigree pointer\n"));
247
248 symlink(framebufferDevice, "subsystem", String("/sys/bus/platform"));
249
250 addBlockDevices(block);
251 addNetworkDevices(network);
252 addPciDevices(pciDevices);
253 if (g_pBootstrapInfo && g_pBootstrapInfo->isUefi() && !directory(firmware, "efi"))
254 return false;
255 return true;
256}
static void foreach(Callback callback, Device *root=0)
Definition Device.cc:107
@ Disk
A disk device - a block device in UNIX terms.
Definition Device.h:75
void emptyCache()
Definition Disk.h:35
Definition File.h:74
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1281
virtual bool isBytewise() const
Definition File.cc:1277
void setGidOnly(size_t gid)
Definition File.cc:1360
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1352
void setUidOnly(size_t uid)
Definition File.cc:1356
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition File.cc:1288
bool initialise(Disk *) override
Definition SysFs.cc:212
const String & getVolumeLabel() const override
Definition SysFs.cc:93
static VFS & instance()
Definition VFS.cc:311
void trackFile(File *pFile)
Track a File object that exists. It is necessary to keep track of File objects, or at least those tha...
Definition VFS.cc:1518
@ ERR_OK
Definition err.h:82
#define netif_is_up(netif)
Definition netif.h:420
#define netif_is_link_up(netif)
Definition netif.h:432
#define NETIF_MAX_HWADDR_LEN
Definition netif.h:82
Definition netif.h:244
err_t tcpip_callback_wait(tcpip_callback_fn function, void *ctx)
Definition tcpip.c:365