2#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
3#include "pedigree/kernel/processor/VirtualAddressSpace.h"
4#include "pedigree/kernel/syscallError.h"
5#include "pedigree/kernel/time/Time.h"
6#include "pedigree/kernel/utilities/SharedPointer.h"
7#include "pedigree/kernel/utilities/assert.h"
8#include "pedigree/kernel/utilities/utility.h"
12#include "PosixSubsystem.h"
13#include "ipc-common.h"
16#include "modules/system/vfs/VFS.h"
17#include "sysv-shm-syscalls.h"
20constexpr size_t MaximumSegments = 128;
21constexpr size_t MaximumSegmentBytes = 16 * 1024 * 1024;
22constexpr size_t MaximumTotalBytes = 64 * 1024 * 1024;
23constexpr size_t MaximumAttachments = 128;
24constexpr int Create = 01000, Exclusive = 02000, HugePages = 04000, NoReserve = 010000;
25constexpr int ReadOnly = 010000, Round = 020000, Remap = 040000, Executable = 0100000;
26constexpr int Remove = 0, Set = 1, Stat = 2, Info = 3;
27constexpr int Lock = 11, Unlock = 12, SegmentStat = 13, SegmentInfo = 14, SegmentStatAny = 15;
28constexpr unsigned Destroyed = 01000, Locked = 02000;
33 int64_t attachTime, detachTime, changeTime;
35 uint64_t attachments, unused[2];
37static_assert(
sizeof(SegmentStatus) == 112,
"Linux amd64 shmid_ds layout");
38static_assert(__builtin_offsetof(SegmentStatus, attachments) == 88,
"Linux shm_nattch offset");
40 uint64_t maximumSize, minimumSize, segmentCount, attachments, totalPages, unused[4];
43 int32_t usedIds, padding;
44 uint64_t totalPages, residentPages, swappedPages, swapAttempts, swapSuccesses;
46static_assert(
sizeof(SegmentLimits) == 72 &&
sizeof(SegmentUsage) == 48,
"Linux shm info layout");
49 uint64_t namespaceId = posix_ipc_namespace_id();
50 Segment(
int identifier,
size_t slot, int32_t key,
size_t size,
size_t roundedSize,
unsigned mode)
53 roundedSize(roundedSize),
55 pendingAttachments(0),
58 file(new
RamFile(
String(
"shm"), identifier, &filesystem, nullptr)) {
59 PosixIpc::initialize(status.permission, key, mode, identifier / MaximumSegments);
62 status.changeTime = Time::getTime();
72 file->returnPhysicalPage(--lockedPages * pageSize);
74 status.permission.mode &= ~Locked;
77 if (status.permission.mode & Locked) {
81 for (
size_t offset = 0; offset < roundedSize; offset += pageSize) {
82 if (file->read(offset, pageSize, 0) != pageSize || file->getPhysicalPage(offset) == ~0UL) {
84 SYSCALL_ERROR(OutOfMemory);
89 status.permission.mode |= Locked;
93 size_t slot, roundedSize, lockedPages, pendingAttachments;
104size_t totalBytes = 0;
105uint32_t nextSequence = 0;
108 if ((segment->status.permission.mode & Destroyed) && !segment->status.attachments &&
109 !segment->pendingAttachments && segments[segment->slot] == segment) {
110 totalBytes -= segment->roundedSize;
111 segments[segment->slot].
reset();
118 : m_Segment(segment), m_Base(0), m_Pid(0), m_Active(false) {}
119 ~ShmAttachment()
override {
124 for (
auto it = attachments.
begin(); it != attachments.
end(); ++it) {
126 attachments.
erase(it);
130 assert(m_Segment->status.attachments);
131 --m_Segment->status.attachments;
132 m_Segment->status.detachTime = Time::getTime();
133 m_Segment->status.lastPid = m_UserPid;
136 void activate(uintptr_t base,
size_t pid,
size_t userPid) {
141 ++m_Segment->status.attachments;
142 m_Segment->status.attachTime = Time::getTime();
143 m_Segment->status.lastPid = userPid;
146 uintptr_t baseAddress()
const override {
149 void relocate(uintptr_t newBase)
override {
156 auto* copy =
new ShmAttachment(m_Segment);
165 size_t m_UserPid = 0;
171 const size_t slot = index ?
static_cast<size_t>(id) : id % MaximumSegments;
172 if (slot < MaximumSegments && segments[slot] &&
173 segments[slot]->namespaceId == posix_ipc_namespace_id() &&
174 (index || segments[slot]->
id ==
id)) {
175 return segments[slot];
178 SYSCALL_ERROR(InvalidArgument);
182int information(
int command,
void* buffer) {
184 SegmentUsage
usage = {};
186 for (
size_t i = 0; i < MaximumSegments; ++i) {
187 if (segments[i] && segments[i]->namespaceId == posix_ipc_namespace_id()) {
190 usage.totalPages += segments[i]->roundedSize / pageSize;
191 usage.residentPages += segments[i]->file->getAttributes().blocks * 512 / pageSize;
194 if (command == Info) {
195 const SegmentLimits limits = {
196 MaximumSegmentBytes, 1, MaximumSegments, MaximumAttachments,
197 MaximumTotalBytes / pageSize, {}};
204 SYSCALL_ERROR(BadAddress);
209int posix_shmget(int32_t key,
size_t size,
int flags) {
211 if (flags & HugePages) {
212 SYSCALL_ERROR(OperationNotSupported);
215 if (flags & ~(0777 | Create | Exclusive | NoReserve)) {
216 SYSCALL_ERROR(InvalidArgument);
219 size_t freeSlot = MaximumSegments;
220 for (
size_t i = 0; i < MaximumSegments; ++i) {
222 if (freeSlot == MaximumSegments) {
227 if (key && segments[i]->namespaceId == posix_ipc_namespace_id() &&
228 segments[i]->status.permission.key == key) {
229 if ((flags & (Create | Exclusive)) == (Create | Exclusive)) {
230 SYSCALL_ERROR(FileExists);
233 if (size > segments[i]->status.size) {
234 SYSCALL_ERROR(InvalidArgument);
237 const unsigned access = ((flags >> 6) | (flags >> 3) | flags) & 7;
238 if (!PosixIpc::allowed(segments[i]->status.permission, access)) {
239 SYSCALL_ERROR(PermissionDenied);
242 return segments[i]->id;
245 if (key && !(flags & Create)) {
246 SYSCALL_ERROR(DoesNotExist);
249 if (!size || size > MaximumSegmentBytes) {
250 SYSCALL_ERROR(InvalidArgument);
254 const size_t roundedSize = (size + pageMask) & ~pageMask;
255 if (freeSlot == MaximumSegments || roundedSize > MaximumTotalBytes - totalBytes ||
256 nextSequence > (0x7fffffffU - freeSlot) / MaximumSegments) {
257 SYSCALL_ERROR(NoSpaceLeftOnDevice);
260 const int id = nextSequence++ * MaximumSegments + freeSlot;
262 if (!segment->file->resize(roundedSize)) {
265 totalBytes += roundedSize;
266 segments[freeSlot] = segment;
270void* posix_shmat(
int id,
const void* requestedAddress,
int flags) {
273 void*
const failed =
reinterpret_cast<void*
>(~static_cast<uintptr_t>(0));
274 if ((flags & ~(ReadOnly | Round | Remap | Executable)) ||
275 ((flags & Remap) && !requestedAddress)) {
276 SYSCALL_ERROR(InvalidArgument);
279 auto segment = findSegment(
id);
283 unsigned access = 4 | (flags & ReadOnly ? 0 : 2) | (flags & Executable ? 1 : 0);
284 if (!PosixIpc::allowed(segment->status.permission, access)) {
285 SYSCALL_ERROR(PermissionDenied);
289 const size_t pid = PosixIpc::process()->
getId();
290 for (
auto it = attachments.
begin(); it != attachments.
end(); ++it) {
291 count += (*it)->pid() == pid;
293 if (count >= MaximumAttachments) {
294 syscallError(EMFILE);
297 uintptr_t address =
reinterpret_cast<uintptr_t
>(requestedAddress);
300 address &= ~pageMask;
303 if ((requestedAddress && !address) || (address & pageMask) ||
306 SYSCALL_ERROR(InvalidArgument);
309 const auto placement = !requestedAddress ? MemoryMapManager::Placement::Hint
310 : flags & Remap ? MemoryMapManager::Placement::FixedReplace
311 : MemoryMapManager::Placement::FixedNoReplace;
313 if (!(flags & ReadOnly)) {
314 permissions |= MemoryMappedObject::Write;
316 if (flags & Executable) {
317 permissions |= MemoryMappedObject::Exec;
319 auto* owner =
new ShmAttachment(segment);
321 MemoryMapManager::MapStatus status;
324 ++segment->pendingAttachments;
325 if (!mappings.
mapFile(segment->file, address, segment->roundedSize, permissions, 0,
false,
326 placement, &status, permissions, attachment)) {
327 --segment->pendingAttachments;
329 syscallError(status == MemoryMapManager::MapStatus::AddressInUse ? Error::InvalidArgument
330 : Error::OutOfMemory);
333 owner->activate(address, pid, PosixIpc::process()->getUserspaceId());
334 --segment->pendingAttachments;
335 return reinterpret_cast<void*
>(address);
338int posix_shmdt(
const void* address) {
341 auto attachment = mappings.
findAttachment(
reinterpret_cast<uintptr_t
>(address));
343 SYSCALL_ERROR(InvalidArgument);
349int posix_shmctl(
int id,
int command,
void* buffer) {
352 if (command == Info || command == SegmentInfo) {
353 return information(command, buffer);
355 const bool index = command == SegmentStat || command == SegmentStatAny;
356 auto segment = findSegment(
id, index);
360 if (command == Stat || index) {
361 if (command != SegmentStatAny && !PosixIpc::allowed(segment->status.permission, 4)) {
362 SYSCALL_ERROR(PermissionDenied);
366 SYSCALL_ERROR(BadAddress);
369 return index ? segment->id : 0;
371 if (command != Remove && command != Set && command != Lock && command != Unlock) {
372 SYSCALL_ERROR(InvalidArgument);
375 if (!PosixIpc::owner(segment->status.permission)) {
376 SYSCALL_ERROR(NotEnoughPermissions);
379 if (command == Remove) {
380 segment->status.permission.mode |= Destroyed;
381 segment->status.permission.key = 0;
383 }
else if (command == Lock) {
384 if (!segment->lock()) {
387 }
else if (command == Unlock) {
390 SegmentStatus input = {};
392 SYSCALL_ERROR(BadAddress);
395 if (input.permission.uid == 0xffffffffU || input.permission.gid == 0xffffffffU) {
396 SYSCALL_ERROR(InvalidArgument);
399 segment->status.permission.uid = input.permission.uid;
400 segment->status.permission.gid = input.permission.gid;
401 segment->status.permission.mode =
402 (segment->status.permission.mode & ~0777U) | (input.permission.mode & 0777);
403 segment->status.changeTime = Time::getTime();
408void posix_shm_namespace_exit(uint64_t
identity) {
410 for (
auto& entry : segments) {
411 if (entry && entry->namespaceId ==
identity) {
412 auto segment = entry;
413 segment->status.permission.mode |= Destroyed;
414 segment->status.permission.key = 0;
Memory-mapped file interface.
SharedPointer< MappingAttachment > findAttachment(uintptr_t base)
static MemoryMapManager & instance()
MemoryMappedObject * mapFile(File *pFile, uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms, size_t offset=0, bool bCopyOnWrite=true)
size_t removeAttachment(const SharedPointer< MappingAttachment > &attachment)
static constexpr size_t getPageSize() PURE
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)
size_t getUserspaceId() const
static ProcessorInformation & information()
bool untrackFile(File *pFile, bool destroy=true)
void trackFile(File *pFile)
Track a File object that exists. It is necessary to keep track of File objects, or at least those tha...
virtual uintptr_t getUserStart() const =0
virtual uintptr_t getKernelStart() const =0
Iterator erase(Iterator &Iter)
void pushBack(const T &value)