The Pedigree Project 0.1
DevFs.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "DevFs.h"
21
22#include "DevFs-block.h"
23#include "InputFile.h"
24#include "PosixSubsystem.h"
25#include "descriptor-path.h"
26#include "linux-fb-abi.h"
27#include "modules/system/vfs/Pipe.h"
28#include "modules/system/vfs/Symlink.h"
29#include "modules/system/vfs/VFS.h"
30
31#define MACHINE_FORWARD_DECL_ONLY
32#include "pedigree/kernel/ServiceManager.h"
33#include "pedigree/kernel/graphics/Graphics.h"
34#include "pedigree/kernel/graphics/GraphicsService.h"
35#include "pedigree/kernel/machine/Display.h"
36#include "pedigree/kernel/machine/Framebuffer.h"
37#include "pedigree/kernel/machine/InputManager.h"
38#include "pedigree/kernel/machine/Machine.h"
39#include "pedigree/kernel/machine/Serial.h"
40#include "pedigree/kernel/machine/Vga.h"
41#include "pedigree/kernel/process/Process.h"
42#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
43#include "pedigree/kernel/syscallError.h"
44#include "pedigree/kernel/utilities/SecureRandom.h"
45#include "pedigree/kernel/utilities/assert.h"
46#include "pedigree/kernel/utilities/lib.h"
47#include "pedigree/kernel/utilities/utility.h"
48
49#include "modules/system/console/Console.h"
50#include <pedigree/fb.h>
51
53#define ALT_KEY (1ULL << 60)
54#define SHIFT_KEY (1ULL << 61)
55#define CTRL_KEY (1ULL << 62)
56#define SPECIAL_KEY (1ULL << 63)
57
58namespace {
59class CttySelectorFile final : public File {
60 public:
61 CttySelectorFile(size_t inode, Filesystem* filesystem, File* parent)
62 : File(String("tty"), 0, 0, 0, inode, filesystem, 0, parent) {
63 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_GW | FILE_OR | FILE_OW);
64 }
65
66 private:
67 bool isBytewise() const override {
68 return true;
69 }
70};
71
72class FullFile final : public ZeroFile {
73 public:
74 FullFile(size_t inode, Filesystem* filesystem, File* parent)
75 : ZeroFile(String("full"), inode, filesystem, parent) {}
76
77 uint64_t writeBytewise(uint64_t, uint64_t, uintptr_t, bool) override {
78 SYSCALL_ERROR(NoSpaceLeftOnDevice);
79 return 0;
80 }
81};
82
83class DeviceLink final : public Symlink {
84 public:
85 DeviceLink(const String& name, const String& target, size_t inode, Filesystem* filesystem,
86 File* parent)
87 : Symlink(name, 0, 0, 0, inode, filesystem, target.length(), parent) {
88 m_sTarget = target;
89 setPermissions(FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR | FILE_OW |
90 FILE_OX);
91 }
92};
93
94void setLinuxBitfield(LinuxFbBitfield& field, uint32_t offset, uint32_t length) {
95 field.offset = offset;
96 field.length = length;
97}
98
99void setLinuxPixelFormat(LinuxFbVariableInfo& info, Graphics::PixelFormat format) {
100 switch (format) {
101 case Graphics::Bits32_Argb:
102 setLinuxBitfield(info.red, 16, 8);
103 setLinuxBitfield(info.green, 8, 8);
104 setLinuxBitfield(info.blue, 0, 8);
105 setLinuxBitfield(info.transparency, 24, 8);
106 break;
107 case Graphics::Bits32_Rgba:
108 setLinuxBitfield(info.red, 24, 8);
109 setLinuxBitfield(info.green, 16, 8);
110 setLinuxBitfield(info.blue, 8, 8);
111 setLinuxBitfield(info.transparency, 0, 8);
112 break;
113 case Graphics::Bits32_Rgb:
114 case Graphics::Bits24_Rgb:
115 setLinuxBitfield(info.red, 16, 8);
116 setLinuxBitfield(info.green, 8, 8);
117 setLinuxBitfield(info.blue, 0, 8);
118 break;
119 case Graphics::Bits32_Bgr:
120 case Graphics::Bits24_Bgr:
121 setLinuxBitfield(info.red, 0, 8);
122 setLinuxBitfield(info.green, 8, 8);
123 setLinuxBitfield(info.blue, 16, 8);
124 break;
125 case Graphics::Bits16_Argb:
126 setLinuxBitfield(info.red, 8, 4);
127 setLinuxBitfield(info.green, 4, 4);
128 setLinuxBitfield(info.blue, 0, 4);
129 setLinuxBitfield(info.transparency, 12, 4);
130 break;
131 case Graphics::Bits16_Rgb565:
132 setLinuxBitfield(info.red, 11, 5);
133 setLinuxBitfield(info.green, 5, 6);
134 setLinuxBitfield(info.blue, 0, 5);
135 break;
136 case Graphics::Bits16_Rgb555:
137 setLinuxBitfield(info.red, 10, 5);
138 setLinuxBitfield(info.green, 5, 5);
139 setLinuxBitfield(info.blue, 0, 5);
140 break;
141 case Graphics::Bits8_Idx:
142 info.red.length = info.green.length = info.blue.length = 8;
143 break;
144 case Graphics::Bits8_Rgb332:
145 setLinuxBitfield(info.red, 5, 3);
146 setLinuxBitfield(info.green, 2, 3);
147 setLinuxBitfield(info.blue, 0, 2);
148 break;
149 }
150}
151
152void fillLinuxVariableInfo(LinuxFbVariableInfo& info, Framebuffer& framebuffer, size_t depth) {
153 info.xResolution = framebuffer.getWidth();
154 info.yResolution = framebuffer.getHeight();
155 info.virtualXResolution = info.xResolution;
156 info.virtualYResolution = info.yResolution;
157 info.bitsPerPixel = depth ? depth : Graphics::bitsPerPixel(framebuffer.getFormat());
158 info.heightMillimeters = UINT32_MAX;
159 info.widthMillimeters = UINT32_MAX;
160 setLinuxPixelFormat(info, framebuffer.getFormat());
161}
162} // namespace
163
164static void terminalSwitchHandler(InputManager::InputNotification& in) {
165 if (!in.meta) {
166 return;
167 }
168
169 DevFs* p = reinterpret_cast<DevFs*>(in.meta);
170 p->handleInput(in);
171}
172
173uint64_t RandomFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
174 bool bCanBlock) {
175 uint8_t snapshot[256];
176 size_t done = 0;
177 while (done < size) {
178 const size_t count = size - done < sizeof(snapshot) ? size - done : sizeof(snapshot);
179 const size_t produced = secure_random_bytes(snapshot, count);
180 if (!produced) {
181 if (!done)
182 SYSCALL_ERROR(NoMoreProcesses);
183 break;
184 }
185 // Device I/O may target a faultable caller buffer. Keep faults outside the
186 // generator's IRQ-disabled critical section.
187 MemoryCopy(reinterpret_cast<void*>(buffer + done), snapshot, produced);
188 done += produced;
189 }
190 pedigree_random::erase(snapshot, sizeof(snapshot));
191 return done;
192}
193
194uint64_t RandomFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
195 bool bCanBlock) {
196 // Entropy feed-back writes must make progress for buffered stdio callers.
197 // Discard untrusted input without crediting it as entropy or seeding the RNG.
198 return size;
199}
200
201bool RandomFile::supports(size_t command) const {
202 return command == 0x40085203UL; // Linux RNDADDENTROPY.
203}
204
205int RandomFile::command(size_t command, void* buffer) {
206 if (!supports(command)) {
207 SYSCALL_ERROR(InvalidArgument);
208 return -1;
209 }
210 auto* process = Processor::information().getCurrentThread()->getParent();
211 if (process->getEffectiveUserId() != 0) {
212 SYSCALL_ERROR(NotEnoughPermissions);
213 return -1;
214 }
215
216 struct SeedRequest {
217 int32_t entropyBits;
218 int32_t bytes;
219 uint8_t seed[32];
220 } request = {};
221 // Only the privileged initializer can claim entropy. Ordinary device
222 // writes cannot turn predictable bytes into a trusted seed.
223 if (!PosixSubsystem::copyFromUser(&request, buffer, sizeof(request))) {
224 pedigree_random::erase(&request, sizeof(request));
225 SYSCALL_ERROR(BadAddress);
226 return -1;
227 }
228 if (request.entropyBits != 256 || request.bytes != 32) {
229 pedigree_random::erase(&request, sizeof(request));
230 SYSCALL_ERROR(InvalidArgument);
231 return -1;
232 }
233 const int accepted = secure_random_seed(request.seed, sizeof(request.seed));
234 pedigree_random::erase(&request, sizeof(request));
235 if (!accepted) {
236 SYSCALL_ERROR(InvalidArgument);
237 return -1;
238 }
239 return 0;
240}
241
242uint64_t NullFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
243 bool bCanBlock) {
244 return 0;
245}
246
247uint64_t NullFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
248 bool bCanBlock) {
249 return size;
250}
251
252PtmxFile::PtmxFile(String str, size_t inode, Filesystem* pParentFS, File* pParent,
253 DevFsDirectory* ptsDirectory, uint32_t slavePermissions, bool callerOwns)
254 : File(str, 0, 0, 0, inode, pParentFS, 0, pParent),
255 m_Terminals(),
256 m_pPtsDirectory(ptsDirectory),
257 m_SlavePermissions(slavePermissions),
258 m_CallerOwns(callerOwns) {
259 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_GW | FILE_OR | FILE_OW);
260 setUidOnly(0);
261 setGidOnly(0);
262}
263
264PtmxFile::~PtmxFile() {
265 for (auto* master : m_Masters) {
266 VFS::instance().untrackFile(master);
267 }
268}
269
270uint64_t PtmxFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
271 bool bCanBlock) {
272 return 0;
273}
274
275uint64_t PtmxFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
276 bool bCanBlock) {
277 return 0;
278}
279
281 LockGuard<Mutex> guard(m_Lock);
282 if (!m_Masters.tryReserve(m_Masters.count() + 1)) {
283 SYSCALL_ERROR(OutOfMemory);
284 return nullptr;
285 }
286 // find a new terminal ID that we can safely use
287 size_t terminal = m_Terminals.getFirstClear();
288
289 // create the terminals
290 String masterName, slaveName;
291 masterName.Format("pty%d", terminal);
292 slaveName.Format("%d", terminal);
293
294 ConsoleMasterFile* pMaster =
295 new ConsoleMasterFile(terminal, masterName, m_pPtsDirectory->getFilesystem());
296 ConsoleSlaveFile* pSlave =
297 new ConsoleSlaveFile(terminal, slaveName, m_pPtsDirectory->getFilesystem(), m_pPtsDirectory);
298 if (!pMaster || !pSlave) {
299 delete pMaster;
300 delete pSlave;
301 SYSCALL_ERROR(OutOfMemory);
302 return nullptr;
303 }
304 pMaster->setInode(getFilesystem() == g_pDevFs ? g_pDevFs->getNextInode() : 3 + terminal * 2);
305 pSlave->setInode(getFilesystem() == g_pDevFs ? g_pDevFs->getNextInode() : 4 + terminal * 2);
306 pSlave->setPermissions(m_SlavePermissions);
307 if (m_CallerOwns) {
308 FilesystemCredentials credentials;
309 if (!Process::currentFilesystemCredentials(credentials)) {
310 delete pMaster;
311 delete pSlave;
312 SYSCALL_ERROR(NotEnoughPermissions);
313 return nullptr;
314 }
315 pMaster->setUid(credentials.uid);
316 pMaster->setGid(credentials.gid);
317 pSlave->setUid(credentials.uid);
318 pSlave->setGid(credentials.gid);
319 }
320
321 pMaster->setOther(pSlave);
322 pSlave->setOther(pMaster);
323
324 if (!m_pPtsDirectory->addEntry(slaveName, pSlave)) {
325 delete pMaster;
326 delete pSlave;
327 SYSCALL_ERROR(OutOfMemory);
328 return nullptr;
329 }
330 VFS::instance().trackFile(pMaster);
331 m_Masters.pushBack(pMaster);
332 m_Terminals.set(terminal);
333
334 // we actually open the newly-created master, which does not exist in the
335 // filesystem at all
336 // The private devpts filesystem owns the pair; descriptor paths and controlling
337 // terminal bindings retain its storage until neither endpoint can be used.
338 return pMaster;
339}
340
341uint64_t ZeroFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
342 bool bCanBlock) {
343 ByteSet(reinterpret_cast<void*>(buffer), 0, size);
344 return size;
345}
346
347uint64_t ZeroFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
348 bool bCanBlock) {
349 return size;
350}
351
352SerialFile::SerialFile(String str, size_t inode, Filesystem* pParentFS, File* pParent,
353 Serial* serial)
354 : File(str, 0, 0, 0, inode, pParentFS, 0, pParent), m_Serial(serial) {
355 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_GW | FILE_OR | FILE_OW);
356 setUidOnly(0);
357 setGidOnly(0);
358}
359
360uint64_t SerialFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
361 bool bCanBlock) {
362 if (!m_Serial || !size) {
363 return 0;
364 }
365
366 char* destination = reinterpret_cast<char*>(buffer);
367 size_t count = 0;
368 if (bCanBlock) {
369 const char c = m_Serial->read();
370 if (!c) {
371 return 0;
372 }
373 destination[count++] = c;
374 }
375
376 while (count < size) {
377 const char c = m_Serial->readNonBlock();
378 if (!c) {
379 break;
380 }
381 destination[count++] = c;
382 }
383 return count;
384}
385
386uint64_t SerialFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
387 bool bCanBlock) {
388 if (!m_Serial) {
389 return 0;
390 }
391
392 const char* source = reinterpret_cast<const char*>(buffer);
393 for (size_t i = 0; i < size; ++i) {
394 m_Serial->write(source[i]);
395 }
396 return size;
397}
398
399int SerialFile::select(bool bWriting, int timeout) {
400 if (bWriting) {
401 return m_Serial ? 1 : 0;
402 }
403 return m_Serial && m_Serial->hasData() ? 1 : 0;
404}
405
406uint64_t RtcFile::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock) {
407 return 0;
408}
409
410uint64_t RtcFile::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
411 bool bCanBlock) {
412 return 0;
413}
414
415bool RtcFile::supports(const size_t command) const {
416 // read/set time
417 return true;
418 // return static_cast<size_t>(command) == 0x80247009UL ||
419 // static_cast<size_t>(command) == 0x4024700aUL;
420}
421
422int RtcFile::command(const size_t command, void* buffer) {
423 NOTICE("RtcFile: command " << Hex << command << " with buffer " << buffer);
424 return 0;
425}
426
427FramebufferFile::FramebufferFile(String str, size_t inode, Filesystem* pParentFS, File* pParentNode)
428 : File(str, 0, 0, 0, inode, pParentFS, 0, pParentNode),
429 m_pGraphicsParameters(0),
430 m_bTextMode(false),
431 m_nDepth(0) {
432 // r/w only for root
433 setPermissionsOnly(FILE_GR | FILE_GW | FILE_UR | FILE_UW);
434 setUidOnly(0);
435 setGidOnly(0);
436 for (size_t i = 0; i < 256; ++i) {
437 m_LinuxPalette[i] = Graphics::createRgb(i, i, i);
438 }
439}
440
441FramebufferFile::~FramebufferFile() {
443}
444
445bool FramebufferFile::initialise() {
446 ServiceFeatures* pFeatures = ServiceManager::instance().enumerateOperations(String("graphics"));
447 Service* pService = ServiceManager::instance().getService(String("graphics"));
448 if (pFeatures && pFeatures->provides(ServiceFeatures::probe)) {
449 if (pService) {
451 m_pGraphicsParameters->wantTextMode = false;
453 sizeof(*m_pGraphicsParameters))) {
456
457 return false;
458 } else {
459 // Set the file size to reflect the size of the framebuffer.
460 setSize(m_pGraphicsParameters->providerResult.pFramebuffer->getHeight() *
461 m_pGraphicsParameters->providerResult.pFramebuffer->getBytesPerLine());
462 setPhysicalMappingFlags(
463 m_pGraphicsParameters->providerResult.pFramebuffer->getMappingFlags());
464
465 Display::ScreenMode currentMode;
466 if (m_pGraphicsParameters->providerResult.pDisplay->getCurrentScreenMode(currentMode)) {
467 m_nDepth = currentMode.pf.nBpp;
468 }
469 }
470 }
471 }
472
473 return pFeatures && pService;
474}
475
476uintptr_t FramebufferFile::readBlock(uint64_t location) {
478 return 0;
479
480 if (location > getSize()) {
481 ERROR("FramebufferFile::readBlock with location > size: " << location);
482 return 0;
483 }
484
486 return reinterpret_cast<uintptr_t>(
487 m_pGraphicsParameters->providerResult.pFramebuffer->getRawBuffer()) +
488 location;
489}
490
491physical_uintptr_t FramebufferFile::getPhysicalPage(size_t offset) {
492 if (!m_pGraphicsParameters || offset >= getSize()) {
493 return ~physical_uintptr_t(0);
494 }
495
496 offset &= ~(PhysicalMemoryManager::getPageSize() - 1);
497 return m_pGraphicsParameters->providerResult.pFramebuffer->getPhysicalPage(offset);
498}
499
501 // Framebuffer pages are direct mappings, not pages borrowed from the file cache.
502}
503
504bool FramebufferFile::supports(const size_t command) const {
505 return ((PEDIGREE_FB_CMD_MIN <= command) && (command <= PEDIGREE_FB_CMD_MAX)) ||
506 command == LinuxFbGetVariableInfo || command == LinuxFbPutVariableInfo ||
507 command == LinuxFbGetFixedInfo || command == LinuxFbGetColorMap ||
508 command == LinuxFbPutColorMap || command == LinuxFbPanDisplay || command == LinuxFbBlank;
509}
510
511int FramebufferFile::command(const size_t command, void* buffer) {
513 ERROR("FramebufferFile::command called on an invalid FramebufferFile");
514 return -1;
515 }
516
517 Display* pDisplay = m_pGraphicsParameters->providerResult.pDisplay;
518 Framebuffer* pFramebuffer = m_pGraphicsParameters->providerResult.pFramebuffer;
519
520 switch (command) {
521 case LinuxFbGetFixedInfo: {
522 LinuxFbFixedInfo value = {};
523 MemoryCopy(value.id, "Pedigree fb", 11);
524 const uint64_t framebufferSize = getSize();
525 value.memoryLength =
526 framebufferSize > UINT32_MAX ? UINT32_MAX : static_cast<uint32_t>(framebufferSize);
527 value.type = LinuxFbPackedPixels;
528 value.visual =
529 pFramebuffer->getFormat() == Graphics::Bits8_Idx ? LinuxFbPseudoColor : LinuxFbTrueColor;
530 value.lineLength = pFramebuffer->getBytesPerLine();
531 if (!PosixSubsystem::copyToUser(buffer, &value, sizeof(value))) {
532 SYSCALL_ERROR(BadAddress);
533 return -1;
534 }
535 return 0;
536 }
537 case LinuxFbGetVariableInfo: {
538 LinuxFbVariableInfo value = {};
539 Display::ScreenMode currentMode;
540 size_t depth = m_nDepth;
541 if (pDisplay->getCurrentScreenMode(currentMode)) {
542 depth = currentMode.pf.nBpp;
543 }
544 fillLinuxVariableInfo(value, *pFramebuffer, depth);
545 if (!PosixSubsystem::copyToUser(buffer, &value, sizeof(value))) {
546 SYSCALL_ERROR(BadAddress);
547 return -1;
548 }
549 return 0;
550 }
551 case LinuxFbPutVariableInfo: {
552 LinuxFbVariableInfo requested = {};
553 if (!PosixSubsystem::copyFromUser(&requested, buffer, sizeof(requested))) {
554 SYSCALL_ERROR(BadAddress);
555 return -1;
556 }
557 if (!requested.xResolution || !requested.yResolution || requested.bitsPerPixel <= 8 ||
558 requested.xOffset || requested.yOffset ||
559 (requested.virtualXResolution && requested.virtualXResolution != requested.xResolution) ||
560 (requested.virtualYResolution && requested.virtualYResolution != requested.yResolution)) {
561 SYSCALL_ERROR(InvalidArgument);
562 return -1;
563 }
564 if ((requested.activate & LinuxFbActivateMask) != LinuxFbActivateTest) {
565 if (!pDisplay->setScreenMode(requested.xResolution, requested.yResolution,
566 requested.bitsPerPixel)) {
567 SYSCALL_ERROR(InvalidArgument);
568 return -1;
569 }
570 m_nDepth = requested.bitsPerPixel;
571 m_bTextMode = false;
572 setSize(pFramebuffer->getHeight() * pFramebuffer->getBytesPerLine());
573 }
574 LinuxFbVariableInfo result = {};
575 fillLinuxVariableInfo(result, *pFramebuffer, m_nDepth);
576 if (!PosixSubsystem::copyToUser(buffer, &result, sizeof(result))) {
577 SYSCALL_ERROR(BadAddress);
578 return -1;
579 }
580 return 0;
581 }
582 case LinuxFbPanDisplay: {
583 LinuxFbVariableInfo requested = {};
584 if (!PosixSubsystem::copyFromUser(&requested, buffer, sizeof(requested))) {
585 SYSCALL_ERROR(BadAddress);
586 return -1;
587 }
588 if (requested.xOffset || requested.yOffset) {
589 SYSCALL_ERROR(InvalidArgument);
590 return -1;
591 }
592 LinuxFbVariableInfo result = {};
593 fillLinuxVariableInfo(result, *pFramebuffer, m_nDepth);
594 if (!PosixSubsystem::copyToUser(buffer, &result, sizeof(result))) {
595 SYSCALL_ERROR(BadAddress);
596 return -1;
597 }
598 return 0;
599 }
600 case LinuxFbGetColorMap: {
601 LinuxFbColorMap map = {};
602 if (!PosixSubsystem::copyFromUser(&map, buffer, sizeof(map))) {
603 SYSCALL_ERROR(BadAddress);
604 return -1;
605 }
606 if (map.start > 256 || map.length > 256 - map.start) {
607 SYSCALL_ERROR(InvalidArgument);
608 return -1;
609 }
610 for (size_t i = 0; i < map.length; ++i) {
611 const uint32_t colour = m_LinuxPalette[map.start + i];
612 const uint16_t red = static_cast<uint16_t>(((colour >> 16) & 0xff) * 0x101);
613 const uint16_t green = static_cast<uint16_t>(((colour >> 8) & 0xff) * 0x101);
614 const uint16_t blue = static_cast<uint16_t>((colour & 0xff) * 0x101);
615 const uint16_t transparency = 0xffff;
616 if (!map.red || !map.green || !map.blue ||
617 !PosixSubsystem::copyToUser(map.red + i, &red, sizeof(red)) ||
618 !PosixSubsystem::copyToUser(map.green + i, &green, sizeof(green)) ||
619 !PosixSubsystem::copyToUser(map.blue + i, &blue, sizeof(blue)) ||
620 (map.transparency && !PosixSubsystem::copyToUser(map.transparency + i, &transparency,
621 sizeof(transparency)))) {
622 SYSCALL_ERROR(BadAddress);
623 return -1;
624 }
625 }
626 return 0;
627 }
628 case LinuxFbPutColorMap: {
629 LinuxFbColorMap map = {};
630 if (!PosixSubsystem::copyFromUser(&map, buffer, sizeof(map))) {
631 SYSCALL_ERROR(BadAddress);
632 return -1;
633 }
634 if (map.start > 256 || map.length > 256 - map.start) {
635 SYSCALL_ERROR(InvalidArgument);
636 return -1;
637 }
638 for (size_t i = 0; i < map.length; ++i) {
639 uint16_t red = 0;
640 uint16_t green = 0;
641 uint16_t blue = 0;
642 if (!map.red || !map.green || !map.blue ||
643 !PosixSubsystem::copyFromUser(&red, map.red + i, sizeof(red)) ||
644 !PosixSubsystem::copyFromUser(&green, map.green + i, sizeof(green)) ||
645 !PosixSubsystem::copyFromUser(&blue, map.blue + i, sizeof(blue))) {
646 SYSCALL_ERROR(BadAddress);
647 return -1;
648 }
649 m_LinuxPalette[map.start + i] = Graphics::createRgb(red >> 8, green >> 8, blue >> 8);
650 }
651 if (pFramebuffer->getFormat() == Graphics::Bits8_Idx) {
652 pFramebuffer->setPalette(m_LinuxPalette, 256);
653 }
654 return 0;
655 }
656 case LinuxFbBlank:
657 return 0;
658 case PEDIGREE_FB_SETMODE: {
659 pedigree_fb_modeset value = {};
660 if (!PosixSubsystem::copyFromUser(&value, buffer, sizeof(value))) {
661 SYSCALL_ERROR(BadAddress);
662 return -1;
663 }
664 const pedigree_fb_modeset* arg = &value;
665 size_t desiredWidth = arg->width;
666 size_t desiredHeight = arg->height;
667 size_t desiredDepth = arg->depth;
668
669 // Are we seeking a text mode?
670 if (!(desiredWidth && desiredHeight && desiredDepth)) {
671 bool bSuccess = false;
672 if (!m_pGraphicsParameters->providerResult.bTextModes) {
673 bSuccess = pDisplay->setScreenMode(0);
674 if (bSuccess) {
675 // Native drivers may restore a larger graphics mode for this request.
676 setSize(pFramebuffer->getHeight() * pFramebuffer->getBytesPerLine());
677 Display::ScreenMode currentMode;
678 if (pDisplay->getCurrentScreenMode(currentMode))
679 m_nDepth = currentMode.pf.nBpp;
680 m_bTextMode = false;
681 if (Machine::instance().getNumVga())
682 Machine::instance().getVga(0)->setLargestTextMode();
683 }
684 } else {
685 // Set via VGA method.
686 if (Machine::instance().getNumVga()) {
687 Vga* pVga = Machine::instance().getVga(0);
688 pVga->setLargestTextMode();
689
690 m_nDepth = 0;
691 m_bTextMode = true;
692
693 bSuccess = true;
694 }
695 }
696
697 if (bSuccess) {
698 NOTICE("FramebufferFile: " << (m_pGraphicsParameters->providerResult.bTextModes
699 ? "set text mode"
700 : "restored graphics mode"));
701 return 0;
702 } else {
703 return -1;
704 }
705 }
706
707 bool bSet = false;
708 while (desiredDepth > 8) {
709 if (pDisplay->setScreenMode(desiredWidth, desiredHeight, desiredDepth)) {
710 NOTICE("FramebufferFile: set mode " << Dec << desiredWidth << "x" << desiredHeight << "x"
711 << desiredDepth << Hex << ".");
712 bSet = true;
713 break;
714 }
715 desiredDepth -= 8;
716 }
717
718 if (bSet) {
719 m_nDepth = desiredDepth;
720
721 setSize(pFramebuffer->getHeight() * pFramebuffer->getBytesPerLine());
722
723 m_bTextMode = false;
724 }
725
726 return bSet ? 0 : -1;
727 }
728 case PEDIGREE_FB_GETMODE: {
729 pedigree_fb_mode value = {};
730 pedigree_fb_mode* arg = &value;
731 if (m_bTextMode) {
732 ByteSet(arg, 0, sizeof(*arg));
733 } else {
734 arg->width = pFramebuffer->getWidth();
735 arg->height = pFramebuffer->getHeight();
736 arg->depth = m_nDepth;
737 arg->bytes_per_pixel = pFramebuffer->getBytesPerPixel();
738 arg->format = pFramebuffer->getFormat();
739 arg->bytes_per_line = pFramebuffer->getBytesPerLine();
740 }
741 if (!PosixSubsystem::copyToUser(buffer, &value, sizeof(value))) {
742 SYSCALL_ERROR(BadAddress);
743 return -1;
744 }
745 return 0;
746 }
747 case PEDIGREE_FB_REDRAW: {
748 if (!buffer) {
749 // Redraw all.
750 pFramebuffer->redraw(0, 0, pFramebuffer->getWidth(), pFramebuffer->getHeight(), true);
751 } else {
752 pedigree_fb_rect value = {};
753 if (!PosixSubsystem::copyFromUser(&value, buffer, sizeof(value))) {
754 SYSCALL_ERROR(BadAddress);
755 return -1;
756 }
757 const pedigree_fb_rect* arg = &value;
758 pFramebuffer->redraw(arg->x, arg->y, arg->w, arg->h, true);
759 }
760
761 return 0;
762 }
763 default:
764 return -1;
765 }
766}
767
768Tty0File::Tty0File(String str, size_t inode, Filesystem* pParentFS, File* pParent, DevFs* devfs)
769 : File(str, 0, 0, 0, inode, pParentFS, 0, pParent), m_pDevFs(devfs) {
770 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_GW | FILE_OR | FILE_OW);
771 setUidOnly(0);
772 setGidOnly(0);
773}
774
775Tty0File::~Tty0File() {}
776
777uint64_t Tty0File::readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
778 bool bCanBlock) {
779 return 0;
780}
781
782uint64_t Tty0File::writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
783 bool bCanBlock) {
784 return 0;
785}
786
788 // easy - just return the currently-active VT
789 return m_pDevFs->getTerminalManager().getCurrentTerminalFile();
790}
791
792physical_uintptr_t MemFile::getPhysicalPage(size_t offset) {
793 // offset is literally the physical page for /dev/mem
794 return offset & ~(PhysicalMemoryManager::getPageSize() - 1);
795}
796
797void MemFile::returnPhysicalPage(size_t offset) {
798 // no-op
799}
800
801DevFsDirectory::~DevFsDirectory() = default;
802
803DevFs::~DevFs() {
804 InputManager::instance().removeCallback(terminalSwitchHandler, this);
805
806 delete m_VtManager;
807 m_pRoot->emptyCache();
808 if (!VFS::instance().untrackFile(m_pRoot)) {
809 ERROR("DevFs: root directory did not get cleaned up");
810 }
811 delete m_Serial;
812}
813
815 // Deterministic inode assignment to each devfs node
816 m_NextInode = 0;
817
818 if (m_pRoot) {
819 delete m_pRoot;
820 }
821
822 m_pRoot = new DevFsDirectory(String(""), 0, 0, 0, getNextInode(), this, 0, 0);
823 // Allow user/group to read and write, but disallow all others anything
824 // other than the ability to list and access files.
825 m_pRoot->setPermissions(FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR |
826 FILE_OX);
827
828 VFS::instance().trackFile(m_pRoot);
829
830 File* block = posix_make_block_directory(*this, m_pRoot);
831 if (!block)
832 return false;
833 m_pRoot->addEntry(block->getName(), block);
834
835 File* disk = posix_make_disk_directory(*this, m_pRoot);
836 if (!disk)
837 return false;
838 m_pRoot->addEntry(disk->getName(), disk);
839
840 File* descriptors = posix_make_dev_fd_link(*this, m_pRoot);
841 if (!descriptors)
842 return false;
843 m_pRoot->addEntry(descriptors->getName(), descriptors);
844
845 m_CttySelector = new CttySelectorFile(getNextInode(), this, m_pRoot);
846 if (!m_CttySelector)
847 return false;
848 m_pRoot->addEntry(m_CttySelector->getName(), m_CttySelector);
849
850 // Create /dev/null and /dev/zero nodes
851 NullFile* pNull = new NullFile(String("null"), getNextInode(), this, m_pRoot);
852 ZeroFile* pZero = new ZeroFile(String("zero"), getNextInode(), this, m_pRoot);
853 FullFile* pFull = new FullFile(getNextInode(), this, m_pRoot);
854 m_pRoot->addEntry(pNull->getName(), pNull);
855 m_pRoot->addEntry(pZero->getName(), pZero);
856 m_pRoot->addEntry(pFull->getName(), pFull);
857
858 auto* pShm = new DevFsDirectory(String("shm"), 0, 0, 0, getNextInode(), this, 0, m_pRoot);
859 pShm->setPermissions(FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GW | FILE_GX | FILE_OR |
860 FILE_OW | FILE_OX | FILE_STICKY);
861 m_pRoot->addEntry(pShm->getName(), pShm);
862
863 const struct {
864 const char* name;
865 const char* target;
866 } standardStreams[] = {
867 {"stdin", "/proc/self/fd/0"}, {"stdout", "/proc/self/fd/1"}, {"stderr", "/proc/self/fd/2"}};
868 for (const auto& stream : standardStreams) {
869 auto* link =
870 new DeviceLink(String(stream.name), String(stream.target), getNextInode(), this, m_pRoot);
871 m_pRoot->addEntry(link->getName(), link);
872 }
873
874 if (Machine::instance().getNumSerial()) {
875 m_Serial = new SerialFile(String("ttyS0"), getNextInode(), this, nullptr,
876 Machine::instance().getSerial(0));
877 auto* console = new ConsolePhysicalFile(~0U, m_Serial, String("ttyS0"), this);
878 m_pRoot->addEntry(console->getName(), console);
879 }
880
881 // Create the /dev/mem device.
882 MemFile* pMem = new MemFile(String("mem"), getNextInode(), this, m_pRoot);
883 m_pRoot->addEntry(pMem->getName(), pMem);
884
885 // Create the /dev/pts directory for ptys to go into.
886 DevFsDirectory* pPts =
887 new DevFsDirectory(String("pts"), 0, 0, 0, getNextInode(), this, 0, m_pRoot);
888 pPts->setPermissions(FILE_UR | FILE_UW | FILE_UX | FILE_GR | FILE_GX | FILE_OR | FILE_OX);
889 m_pRoot->addEntry(pPts->getName(), pPts);
890
891 // Create the /dev/ptmx device.
892 PtmxFile* pPtmx = new PtmxFile(String("ptmx"), getNextInode(), this, m_pRoot, pPts);
893 m_pRoot->addEntry(pPtmx->getName(), pPtmx);
894
895 // Create /dev/urandom for the RNG.
896 RandomFile* pUrandom = new RandomFile(String("urandom"), getNextInode(), this, m_pRoot);
897 m_pRoot->addEntry(pUrandom->getName(), pUrandom);
898
899 // Create /dev/random - note, won't block waiting for more entropy!
900 RandomFile* pRandom = new RandomFile(String("random"), getNextInode(), this, m_pRoot);
901 m_pRoot->addEntry(pRandom->getName(), pRandom);
902
903 // Linux framebuffer userspace expects the primary device at /dev/fb0.
904 FramebufferFile* pFb = new FramebufferFile(String("fb0"), getNextInode(), this, m_pRoot);
905 const bool framebufferAvailable = pFb->initialise();
906 if (framebufferAvailable)
907 m_pRoot->addEntry(pFb->getName(), pFb);
908 else {
909 WARNING("POSIX: no /dev/fb0 - framebuffer failed to initialise.");
910 revertInode();
911 delete pFb;
912 }
913 if (framebufferAvailable) {
914 auto* pFbAlias = new DeviceLink(String("fb"), String("fb0"), getNextInode(), this, m_pRoot);
915 m_pRoot->addEntry(pFbAlias->getName(), pFbAlias);
916 }
917
918 m_VtManager = new VirtualTerminalManager(m_pRoot);
919 if (!m_VtManager->initialise()) {
920 WARNING("POSIX: no /dev/tty - VT manager failed to initialise");
921 delete m_VtManager;
922 m_VtManager = nullptr;
923 }
924
925 // tty0 == current console
926 Tty0File* pTty0 = new Tty0File(String("tty0"), getNextInode(), this, m_pRoot, this);
927 m_pRoot->addEntry(pTty0->getName(), pTty0);
928
929 File* pConsole = nullptr;
930 if (m_VtManager) {
931 pConsole = new Tty0File(String("console"), getNextInode(), this, m_pRoot, this);
932 } else {
933 pConsole = new DeviceLink(String("console"), String("ttyS0"), getNextInode(), this, m_pRoot);
934 }
935 m_pRoot->addEntry(pConsole->getName(), pConsole);
936
937 Pipe* initctl = new Pipe(String("initctl"), 0, 0, 0, getNextInode(), this, 0, m_pRoot);
938 m_pRoot->addEntry(initctl->getName(), initctl);
939 // initctl->increaseRefCount(false); // pretend to be a reader
940
941 RtcFile* rtc = new RtcFile(getNextInode(), this, m_pRoot);
942 m_pRoot->addEntry(rtc->getName(), rtc);
943
944 auto* pInputDirectory =
945 new DevFsDirectory(String("input"), 0, 0, 0, getNextInode(), this, 0, m_pRoot);
946 pInputDirectory->setPermissions(FILE_UR | FILE_UX | FILE_GR | FILE_GX | FILE_OR | FILE_OX);
947 m_pRoot->addEntry(pInputDirectory->getName(), pInputDirectory);
948
949 InputFile* pInput = new InputFile(String("pedigree"), getNextInode(), this, pInputDirectory);
950 if (pInput && pInput->initialise()) {
951 pInputDirectory->addEntry(pInput->getName(), pInput);
952 } else {
953 revertInode();
954 delete pInput;
955 }
956
957 auto* keyboard =
958 new EvdevFile(String("event0"), getNextInode(), this, pInputDirectory, EvdevFile::Keyboard);
959 if (keyboard && keyboard->initialise()) {
960 pInputDirectory->addEntry(keyboard->getName(), keyboard);
961 } else {
962 revertInode();
963 delete keyboard;
964 }
965
966 auto* pointer =
967 new EvdevFile(String("event1"), getNextInode(), this, pInputDirectory, EvdevFile::Pointer);
968 if (pointer && pointer->initialise()) {
969 pInputDirectory->addEntry(pointer->getName(), pointer);
970 } else {
971 revertInode();
972 delete pointer;
973 }
974
975 auto* tablet = new EvdevFile(String("event2"), getNextInode(), this, pInputDirectory,
976 EvdevFile::AbsolutePointer);
977 if (tablet && tablet->initialise()) {
978 pInputDirectory->addEntry(tablet->getName(), tablet);
979 } else {
980 revertInode();
981 delete tablet;
982 }
983
984 EMIT_IF(X86_COMMON) {
985 PsAuxFile* pPsAux = new PsAuxFile(String("psaux"), getNextInode(), this, m_pRoot);
986 if (pPsAux->initialise()) {
987 m_pRoot->addEntry(pPsAux->getName(), pPsAux);
988 m_pPsAuxFile = pPsAux;
989 } else {
990 delete pPsAux;
991 }
992 }
993
994 // add input handler for terminal switching
995 InputManager::instance().installCallback(InputManager::Key, terminalSwitchHandler, this);
996
997 return true;
998}
999
1000size_t DevFs::getNextInode() {
1001 return __atomic_fetch_add(&m_NextInode, 1, __ATOMIC_RELAXED);
1002}
1003
1004void DevFs::revertInode() {
1005 __atomic_fetch_sub(&m_NextInode, 1, __ATOMIC_RELAXED);
1006}
1007
1008void DevFs::handleInput(InputManager::InputNotification& in) {
1009 uint64_t c = in.data.key.key;
1010 if (!m_VtManager || (c & (CTRL_KEY | ALT_KEY)) != (CTRL_KEY | ALT_KEY)) {
1011 return;
1012 }
1013 if (c & SPECIAL_KEY) {
1014 uint32_t k = c & 0xFFFFFFFFUL;
1015 char* s = reinterpret_cast<char*>(&k);
1016
1017 size_t newTty = 0;
1018 if (!StringCompareN(s, "f1", 3)) {
1019 newTty = 0;
1020 } else if (!StringCompareN(s, "f2", 3)) {
1021 newTty = 1;
1022 } else if (!StringCompareN(s, "f3", 3)) {
1023 newTty = 2;
1024 } else if (!StringCompareN(s, "f4", 3)) {
1025 newTty = 3;
1026 } else if (!StringCompareN(s, "f5", 3)) {
1027 newTty = 4;
1028 } else if (!StringCompareN(s, "f6", 3)) {
1029 newTty = 5;
1030 } else {
1031 return;
1032 }
1033
1034 m_VtManager->activate(newTty);
1035 }
1036}
Definition DevFs.h:269
virtual bool initialise(Disk *pDisk)
Definition DevFs.cc:814
void emptyCache()
Definition Disk.h:35
virtual bool getCurrentScreenMode(ScreenMode &sm)
Definition Display.cc:82
virtual bool setScreenMode(ScreenMode sm)
Definition Display.cc:90
void set(size_t n)
Definition File.h:75
String getName() const
Definition File.cc:782
virtual bool isBytewise() const
Definition File.cc:1365
void setGidOnly(size_t gid)
Definition File.cc:1448
void setPermissionsOnly(uint32_t perms)
Definition File.cc:1440
void setUidOnly(size_t uid)
Definition File.cc:1444
GraphicsService::GraphicsParameters * m_pGraphicsParameters
Definition DevFs.h:206
void returnPhysicalPage(size_t offset) override
Definition DevFs.cc:500
virtual int command(const size_t command, void *buffer)
Definition DevFs.cc:511
virtual bool supports(const size_t command) const
Definition DevFs.cc:504
virtual physical_uintptr_t getPhysicalPage(size_t offset)
Definition DevFs.cc:491
virtual uintptr_t readBlock(uint64_t location)
Definition DevFs.cc:476
virtual physical_uintptr_t getPhysicalPage(size_t offset) const
virtual void * getRawBuffer() const
void setPalette(uint32_t *palette, size_t nEntries)
void redraw(size_t x=~0UL, size_t y=~0UL, size_t w=~0UL, size_t h=~0UL, bool bChild=false)
static const int Key
static InputManager & instance()
Singleton design.
void installCallback(CallbackType filter, callback_t callback, void *meta=0)
Installs a callback.
void removeCallback(callback_t callback, void *meta=0)
Removes a callback.
virtual Vga * getVga(size_t n)=0
virtual void returnPhysicalPage(size_t offset)
Definition DevFs.cc:797
virtual physical_uintptr_t getPhysicalPage(size_t offset)
Definition DevFs.cc:792
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:247
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:242
Definition Pipe.h:36
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:270
virtual File * open()
Definition DevFs.cc:280
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:275
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:173
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:194
bool supports(size_t command) const override
Definition DevFs.cc:201
int command(size_t command, void *buffer) override
Definition DevFs.cc:205
virtual int command(const size_t command, void *buffer)
Definition DevFs.cc:422
virtual bool supports(const size_t command) const
Definition DevFs.cc:415
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:410
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:406
virtual int select(bool bWriting=false, int timeout=0)
Definition DevFs.cc:399
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:386
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:360
virtual bool provides(Type service)
Service * getService(const String &serviceName)
ServiceFeatures * enumerateOperations(const String &serviceName)
virtual bool serve(ServiceFeatures::Type type, void *pData, size_t dataLen)=0
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:782
virtual File * open()
Definition DevFs.cc:787
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:777
bool untrackFile(File *pFile, bool destroy=true)
Definition VFS.cc:1670
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:1625
virtual bool setLargestTextMode()=0
bool activate(size_t n)
Starts the process of activating the given tty.
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:341
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition DevFs.cc:347
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270
uint8_t nBpp
Bits per pixel (total).
Definition Display.h:62
All zeroes = 'revert to text mode'.
Definition fb.h:51