Started work on services

This commit is contained in:
wheremyfoodat 2022-09-17 19:51:30 +03:00
parent 3259c5c7a6
commit 208c18356b
9 changed files with 169 additions and 18 deletions

View file

@ -59,6 +59,7 @@ void Kernel::reset() {
}
objects.clear();
portHandles.clear();
serviceManager.reset();
// Allocate handle #0 to a dummy object and make a main process object
makeObject(KernelObjectType::Dummy);
@ -68,6 +69,12 @@ void Kernel::reset() {
makePort("srv:");
}
// Get pointer to thread-local storage
// TODO: Every thread should have its own TLS. We need to adjust for this when we add threads
u32 Kernel::getTLSPointer() {
return VirtualAddrs::TLSBase;
}
// Result CreateAddressArbiter(Handle* arbiter)
void Kernel::createAddressArbiter() {
printf("Stubbed call to CreateAddressArbiter. Handle address: %08X\n", regs[0]);

View file

@ -7,15 +7,40 @@ Handle Kernel::makePort(const char* name) {
objects[ret].data = new PortData();
const auto data = static_cast<PortData*>(objects[ret].data);
std::strncpy(data->name, name, PortData::maxNameLen);
// If the name is empty (ie the first char is the null terminator) then the port is private
data->isPublic = name[0] != '\0';
std::strncpy(data->name, name, PortData::maxNameLen);
// Check if this a special port (Like the service manager port) or not, and cache its type
if (std::strncmp(name, "srv:", PortData::maxNameLen) == 0) {
data->type = PortType::ServiceManager;
} else {
data->type = PortType::Generic;
Helpers::panic("Created generic port %s\n", name);
}
// printf("Created %s port \"%s\" with handle %d\n", data->isPublic ? "public" : "private", data->name, ret);
return ret;
}
Handle Kernel::makeSession(Handle portHandle) {
const auto port = getObject(portHandle, KernelObjectType::Port);
if (port == nullptr) [[unlikely]] {
Helpers::panic("Trying to make session for non-existent port");
}
// Allocate data for session
const Handle ret = makeObject(KernelObjectType::Session);
objects[ret].data = new SessionData();
auto sessionData = static_cast<SessionData*>(objects[ret].data);
// Link session with its parent port
sessionData->portHandle = portHandle;
return ret;
}
// Get the handle of a port based on its name
// If there's no such port, return nullopt
std::optional<Handle> Kernel::getPortHandle(const char* name) {
@ -32,25 +57,33 @@ std::optional<Handle> Kernel::getPortHandle(const char* name) {
// Result ConnectToPort(Handle* out, const char* portName)
void Kernel::connectToPort() {
const u32 handlePointer = regs[0];
const char* port = static_cast<const char*>(mem.getReadPointer(regs[1]));
printf("ConnectToPort(handle pointer = %08X, port = \"%s\")\n", handlePointer, port);
// Read up to max + 1 characters to see if the name is too long
std::string port = mem.readString(regs[1], PortData::maxNameLen + 1);
// TODO: This is unsafe
if (std::strlen(port) > PortData::maxNameLen) {
if (port.size() > PortData::maxNameLen) {
Helpers::panic("ConnectToPort: Port name too long\n");
regs[0] = SVCResult::PortNameTooLong;
return;
}
const auto portHandle = getPortHandle(port);
if (!portHandle.has_value()) [[unlikely]] {
// Try getting a handle to the port
std::optional<Handle> optionalHandle = getPortHandle(port.c_str());
if (!optionalHandle.has_value()) [[unlikely]] {
Helpers::panic("ConnectToPort: Port doesn't exist\n");
regs[0] = SVCResult::ObjectNotFound;
return;
}
Handle portHandle = optionalHandle.value();
printf("ConnectToPort(handle pointer = %08X, port = \"%s\")\n", handlePointer, port.c_str());
const auto portData = static_cast<PortData*>(objects[portHandle].data);
if (!portData->isPublic) {
Helpers::panic("ConnectToPort: Attempted to connect to private port");
}
// TODO: Actually create session
Handle sessionHandle = makeObject(KernelObjectType::Session);
Handle sessionHandle = makeSession(portHandle);
// TODO: Should the result be written back to [r0]?
regs[0] = SVCResult::Success;
@ -70,8 +103,14 @@ void Kernel::sendSyncRequest() {
}
const auto sessionData = static_cast<SessionData*>(session->data);
const u32 messagePointer = VirtualAddrs::TLSBase + 0x80;
const auto port = objects[sessionData->portHandle]; // Get parent port object
const auto portData = static_cast<PortData*>(port.data);
if (portData->type == PortType::ServiceManager) { // Special-case SendSyncRequest targetting "srv:"
serviceManager.handleSyncRequest(getTLSPointer());
} else {
Helpers::panic("SendSyncRequest targetting port %s\n", portData->name);
}
Helpers::panic("SendSyncRequest: Message header: %08X", mem.read32(messagePointer));
regs[0] = SVCResult::Success;
}

View file

@ -202,4 +202,20 @@ void* Memory::getWritePointer(u32 address) {
uintptr_t pointer = writeTable[page];
if (pointer == 0) return nullptr;
return (void*)(pointer + offset);
}
// Thank you Citra devs
std::string Memory::readString(u32 address, u32 maxSize) {
std::string string;
string.reserve(maxSize);
for (std::size_t i = 0; i < maxSize; ++i) {
char c = read8(address++);
if (c == '\0')
break;
string.push_back(c);
}
string.shrink_to_fit();
return string;
}

View file

@ -0,0 +1,59 @@
#include "services/service_manager.hpp"
ServiceManager::ServiceManager(std::array<u32, 16>& regs, Memory& mem) : regs(regs), mem(mem) {}
void ServiceManager::reset() {
}
// Match IPC messages to a "srv:" command based on their header
namespace Commands {
enum : u32 {
RegisterClient = 0x00010002,
EnableNotification = 0x00020000,
RegisterService = 0x00030100,
UnregisterService = 0x000400C0,
GetServiceHandle = 0x00050100,
RegisterPort = 0x000600C2,
UnregisterPort = 0x000700C0,
GetPort = 0x00080100,
Subscribe = 0x00090040,
Unsubscribe = 0x000A0040,
ReceiveNotification = 0x000B0000,
PublishToSubscriber = 0x000C0080,
PublishAndGetSubscriber = 0x000D0040,
IsServiceRegistered = 0x000E00C0
};
}
// Handle an IPC message issued using the SendSyncRequest SVC
// The parameters are stored in thread-local storage in this format: https://www.3dbrew.org/wiki/IPC#Message_Structure
// TLSPointer: The base pointer for this thread's thread-local storage
void ServiceManager::handleSyncRequest(u32 TLSPointer) {
// The message is stored at TLS+0x80 in this format: https://www.3dbrew.org/wiki/IPC#Message_Structure
const u32 messagePointer = TLSPointer + 0x80;
const u32 header = mem.read32(messagePointer);
switch (header) {
case Commands::RegisterClient: registerClient(messagePointer); break;
case Commands::GetServiceHandle: getServiceHandle(messagePointer); break;
default: Helpers::panic("Unknown \"srv:\" command: %08X", header);
}
}
void ServiceManager::registerClient(u32 messagePointer) {
printf("srv: registerClient (Stubbed)\n");
}
void ServiceManager::getServiceHandle(u32 messagePointer) {
printf("srv: getServiceHandle\n");
std::string myBalls;
myBalls.resize(8);
for (int i = 0; i < 8; i++) {
myBalls[i] = mem.read8(messagePointer + 4 + i);
}
std::cout << "Requested handle for service " << myBalls << "\n";
}