[Kernel] Implement CreateMemoryBlock

This commit is contained in:
wheremyfoodat 2023-01-29 16:29:45 +02:00
parent ec7c0b86b7
commit 980139e588
5 changed files with 93 additions and 7 deletions

View file

@ -45,6 +45,7 @@ class Kernel {
Handle makePort(const char* name);
Handle makeSession(Handle port);
Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u32 arg,ThreadStatus status = ThreadStatus::Dormant);
Handle makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission);
public:
Handle makeEvent(ResetType resetType); // Needs to be public to be accessible to the APT/HID services

View file

@ -17,13 +17,17 @@ namespace SVCResult {
BadHandle = 0xD8E007F7,
BadHandleAlt = 0xD9001BF7,
InvalidCombination = 0xE0E01BEE, // Used for invalid memory permission combinations
UnalignedAddr = 0xE0E01BF1,
UnalignedSize = 0xE0E01BF2,
BadThreadPriority = 0xE0E01BFD,
PortNameTooLong = 0xE0E0181E,
};
}
enum class KernelObjectType : u8 {
AddressArbiter, Archive, File, Process, ResourceLimit, Session, Dummy,
AddressArbiter, Archive, File, MemoryBlock, Process, ResourceLimit, Session, Dummy,
// Bundle waitable objects together in the enum to let the compiler optimize certain checks better
Event, Mutex, Port, Semaphore, Timer, Thread
};
@ -142,6 +146,7 @@ static const char* kernelObjectTypeToString(KernelObjectType t) {
case KernelObjectType::Archive: return "archive";
case KernelObjectType::Event: return "event";
case KernelObjectType::File: return "file";
case KernelObjectType::MemoryBlock: return "memory block";
case KernelObjectType::Port: return "port";
case KernelObjectType::Process: return "process";
case KernelObjectType::ResourceLimit: return "resource limit";
@ -168,6 +173,17 @@ struct Semaphore {
Semaphore(u32 initialCount, u32 maximumCount) : initialCount(initialCount), maximumCount(maximumCount) {}
};
struct MemoryBlock {
u32 addr = 0;
u32 size = 0;
u32 myPermission = 0;
u32 otherPermission = 0;
bool mapped = false;
MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm) : addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm),
mapped(false) {}
};
// Generic kernel object class
struct KernelObject {
Handle handle = 0; // A u32 the OS will use to identify objects

View file

@ -10,6 +10,7 @@ class MICService {
MAKE_LOG_FUNCTION(log, micLogger)
// Service commands
void mapSharedMem(u32 messagePointer);
public:
MICService(Memory& mem) : mem(mem) {}

View file

@ -16,6 +16,21 @@ namespace Operation {
};
}
namespace MemoryPermissions {
enum : u32 {
None = 0, // ---
Read = 1, // R--
Write = 2, // -W-
ReadWrite = 3, // RW-
Execute = 4, // --X
ReadExecute = 5, // R-X
WriteExecute = 6, // -WX
ReadWriteExecute = 7, // RWX
DontCare = 0x10000000
};
}
// Returns whether "value" is aligned to a page boundary (Ie a boundary of 4096 bytes)
static constexpr bool isAligned(u32 value) {
return (value & 0xFFF) == 0;
@ -32,8 +47,8 @@ void Kernel::controlMemory() {
u32 size = regs[3];
u32 perms = regs[4];
if (perms == 0x10000000) {
perms = 3; // We make "don't care" equivalent to read-write
if (perms == MemoryPermissions::DontCare) {
perms = MemoryPermissions::ReadWrite; // We make "don't care" equivalent to read-write
Helpers::panic("Unimplemented allocation permission: DONTCARE");
}
@ -126,13 +141,56 @@ void Kernel::mapMemoryBlock() {
regs[0] = SVCResult::Success;
}
Handle Kernel::makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission) {
Handle ret = makeObject(KernelObjectType::MemoryBlock);
objects[ret].data = new MemoryBlock(addr, size, myPermission, otherPermission);
return ret;
}
void Kernel::createMemoryBlock() {
const u32 addr = regs[1];
const u32 size = regs[2];
const u32 myPermission = regs[3];
const u32 otherPermission = regs[4];
u32 myPermission = regs[3];
u32 otherPermission = mem.read32(regs[13] + 4); // This is placed on the stack rather than r4
logSVC("CreateMemoryBlock (addr = %08X, size = %08X, myPermission = %d, otherPermission = %d)\n", addr, size, myPermission, otherPermission);
regs[0] = SVCResult::Success; regs[1] = 0x66666666;
//Helpers::panic("Kernel::CreateMemoryBlock");
// Returns whether a permission is valid
auto isPermValid = [](u32 permission) {
switch (permission) {
case MemoryPermissions::None:
case MemoryPermissions::Read:
case MemoryPermissions::Write:
case MemoryPermissions::ReadWrite:
case MemoryPermissions::DontCare:
return true;
default: // Permissions with the executable flag enabled or invalid permissions are not allowed
return false;
}
};
// Throw error if the size of the shared memory block is not aligned to page boundary
if (!isAligned(size)) {
regs[0] = SVCResult::UnalignedSize;
return;
}
// Throw error if one of the permissions is not valid
if (!isPermValid(myPermission) || !isPermValid(otherPermission)) {
regs[0] = SVCResult::InvalidCombination;
return;
}
// TODO: The address needs to be in a specific range otherwise it throws an invalid address error
if (addr == 0)
Helpers::panic("CreateMemoryBlock: Tried to use addr = 0");
// Implement "Don't care" permission as RW
if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite;
if (otherPermission == MemoryPermissions::DontCare) otherPermission = MemoryPermissions::ReadWrite;
regs[0] = SVCResult::Success;
regs[1] = makeMemoryBlock(addr, size, myPermission, otherPermission);
}

View file

@ -2,6 +2,7 @@
namespace MICCommands {
enum : u32 {
MapSharedMem = 0x00010042
};
}
@ -16,6 +17,15 @@ void MICService::reset() {}
void MICService::handleSyncRequest(u32 messagePointer) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case MICCommands::MapSharedMem: mapSharedMem(messagePointer); break;
default: Helpers::panic("MIC service requested. Command: %08X\n", command);
}
}
void MICService::mapSharedMem(u32 messagePointer) {
u32 size = mem.read32(messagePointer + 4);
u32 handle = mem.read32(messagePointer + 12);
log("MIC::MapSharedMem (size = %08X, handle = %X) (stubbed)\n", size, handle);
mem.write32(messagePointer + 4, Result::Success);
}