mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 07:05:40 +12:00
Started work on services
This commit is contained in:
parent
3259c5c7a6
commit
208c18356b
9 changed files with 169 additions and 18 deletions
|
@ -44,17 +44,16 @@ else()
|
||||||
message(FATAL_ERROR "THIS IS NOT x64 WAIT FOR THE KVM IMPLEMENTATION")
|
message(FATAL_ERROR "THIS IS NOT x64 WAIT FOR THE KVM IMPLEMENTATION")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(SOURCE_FILES src/main.cpp src/emulator.cpp src/core/CPU/cpu_dynarmic.cpp src/core/memory.cpp src/core/elf.cpp
|
set(SOURCE_FILES src/main.cpp src/emulator.cpp src/core/CPU/cpu_dynarmic.cpp src/core/memory.cpp src/core/elf.cpp)
|
||||||
|
|
||||||
)
|
|
||||||
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
||||||
src/core/kernel/memory_management.cpp src/core/kernel/ports.cpp
|
src/core/kernel/memory_management.cpp src/core/kernel/ports.cpp
|
||||||
)
|
)
|
||||||
|
set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp)
|
||||||
|
|
||||||
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp
|
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp
|
||||||
include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/kernel/kernel.hpp
|
include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/kernel/kernel.hpp
|
||||||
include/dynarmic_cp15.hpp include/kernel/resource_limits.hpp include/kernel/kernel_types.hpp
|
include/dynarmic_cp15.hpp include/kernel/resource_limits.hpp include/kernel/kernel_types.hpp
|
||||||
include/kernel/config_mem.hpp
|
include/kernel/config_mem.hpp include/services/service_manager.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
||||||
|
@ -69,7 +68,8 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
||||||
source_group("Header Files\\Core" FILES ${HEADER_FILES})
|
source_group("Header Files\\Core" FILES ${HEADER_FILES})
|
||||||
source_group("Source Files\\Core" FILES ${SOURCE_FILES})
|
source_group("Source Files\\Core" FILES ${SOURCE_FILES})
|
||||||
source_group("Source Files\\Core\\Kernel" FILES ${KERNEL_SOURCE_FILES})
|
source_group("Source Files\\Core\\Kernel" FILES ${KERNEL_SOURCE_FILES})
|
||||||
|
source_group("Source Files\\Core\\Services" FILES ${SERVICE_SOURCE_FILES})
|
||||||
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})
|
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})
|
||||||
|
|
||||||
add_executable(Alber ${SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES})
|
add_executable(Alber ${SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES})
|
||||||
target_link_libraries(Alber PRIVATE sfml-system sfml-network sfml-graphics sfml-window dynarmic)
|
target_link_libraries(Alber PRIVATE sfml-system sfml-network sfml-graphics sfml-window dynarmic)
|
|
@ -6,6 +6,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "services/service_manager.hpp"
|
||||||
|
|
||||||
class Kernel {
|
class Kernel {
|
||||||
std::array<u32, 16>& regs;
|
std::array<u32, 16>& regs;
|
||||||
|
@ -17,6 +18,7 @@ class Kernel {
|
||||||
std::vector<Handle> portHandles;
|
std::vector<Handle> portHandles;
|
||||||
|
|
||||||
u32 currentProcess;
|
u32 currentProcess;
|
||||||
|
ServiceManager serviceManager;
|
||||||
|
|
||||||
// Get pointer to the object with the specified handle
|
// Get pointer to the object with the specified handle
|
||||||
KernelObject* getObject(u32 handle) {
|
KernelObject* getObject(u32 handle) {
|
||||||
|
@ -49,12 +51,14 @@ class Kernel {
|
||||||
|
|
||||||
Handle makeProcess();
|
Handle makeProcess();
|
||||||
Handle makePort(const char* name);
|
Handle makePort(const char* name);
|
||||||
|
Handle makeSession(Handle port);
|
||||||
std::optional<Handle> getPortHandle(const char* name);
|
std::optional<Handle> getPortHandle(const char* name);
|
||||||
void deleteObjectData(KernelObject& object);
|
void deleteObjectData(KernelObject& object);
|
||||||
|
|
||||||
KernelObject* getProcessFromPID(Handle handle);
|
KernelObject* getProcessFromPID(Handle handle);
|
||||||
s32 getCurrentResourceValue(const KernelObject* limit, u32 resourceName);
|
s32 getCurrentResourceValue(const KernelObject* limit, u32 resourceName);
|
||||||
u32 getMaxForResource(const KernelObject* limit, u32 resourceName);
|
u32 getMaxForResource(const KernelObject* limit, u32 resourceName);
|
||||||
|
u32 getTLSPointer();
|
||||||
std::string getProcessName(u32 pid);
|
std::string getProcessName(u32 pid);
|
||||||
|
|
||||||
// SVC implementations
|
// SVC implementations
|
||||||
|
@ -68,7 +72,7 @@ class Kernel {
|
||||||
void connectToPort();
|
void connectToPort();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Kernel(std::array<u32, 16>& regs, Memory& mem) : regs(regs), mem(mem), handleCounter(0) {
|
Kernel(std::array<u32, 16>& regs, Memory& mem) : regs(regs), mem(mem), handleCounter(0), serviceManager(regs, mem) {
|
||||||
objects.reserve(512); // Make room for a few objects to avoid further memory allocs later
|
objects.reserve(512); // Make room for a few objects to avoid further memory allocs later
|
||||||
portHandles.reserve(32);
|
portHandles.reserve(32);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,15 +57,21 @@ struct ProcessData {
|
||||||
ResourceLimits limits;
|
ResourceLimits limits;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class PortType {
|
||||||
|
Generic,
|
||||||
|
ServiceManager // For the service manager port "srv:"
|
||||||
|
};
|
||||||
|
|
||||||
struct PortData {
|
struct PortData {
|
||||||
static constexpr u32 maxNameLen = 11;
|
static constexpr u32 maxNameLen = 11;
|
||||||
|
|
||||||
char name[maxNameLen + 1] = {};
|
char name[maxNameLen + 1] = {};
|
||||||
bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort.
|
bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort.
|
||||||
|
PortType type = PortType::Generic;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SessionData {
|
struct SessionData {
|
||||||
|
Handle portHandle; // The port this session is subscribed to
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* kernelObjectTypeToString(KernelObjectType t) {
|
static const char* kernelObjectTypeToString(KernelObjectType t) {
|
||||||
|
|
|
@ -74,4 +74,6 @@ public:
|
||||||
static constexpr bool isAligned(u32 addr) {
|
static constexpr bool isAligned(u32 addr) {
|
||||||
return (addr & pageMask) == 0;
|
return (addr & pageMask) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string readString(u32 vaddr, u32 maxCharacters);
|
||||||
};
|
};
|
18
include/services/service_manager.hpp
Normal file
18
include/services/service_manager.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
#include <array>
|
||||||
|
#include "helpers.hpp"
|
||||||
|
#include "memory.hpp"
|
||||||
|
|
||||||
|
class ServiceManager {
|
||||||
|
std::array<u32, 16>& regs;
|
||||||
|
Memory& mem;
|
||||||
|
|
||||||
|
// "srv:" commands
|
||||||
|
void getServiceHandle(u32 messagePointer);
|
||||||
|
void registerClient(u32 messagePointer);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ServiceManager(std::array<u32, 16>& regs, Memory& mem);
|
||||||
|
void reset();
|
||||||
|
void handleSyncRequest(u32 TLSPointer);
|
||||||
|
};
|
|
@ -59,6 +59,7 @@ void Kernel::reset() {
|
||||||
}
|
}
|
||||||
objects.clear();
|
objects.clear();
|
||||||
portHandles.clear();
|
portHandles.clear();
|
||||||
|
serviceManager.reset();
|
||||||
|
|
||||||
// Allocate handle #0 to a dummy object and make a main process object
|
// Allocate handle #0 to a dummy object and make a main process object
|
||||||
makeObject(KernelObjectType::Dummy);
|
makeObject(KernelObjectType::Dummy);
|
||||||
|
@ -68,6 +69,12 @@ void Kernel::reset() {
|
||||||
makePort("srv:");
|
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)
|
// Result CreateAddressArbiter(Handle* arbiter)
|
||||||
void Kernel::createAddressArbiter() {
|
void Kernel::createAddressArbiter() {
|
||||||
printf("Stubbed call to CreateAddressArbiter. Handle address: %08X\n", regs[0]);
|
printf("Stubbed call to CreateAddressArbiter. Handle address: %08X\n", regs[0]);
|
||||||
|
|
|
@ -7,15 +7,40 @@ Handle Kernel::makePort(const char* name) {
|
||||||
|
|
||||||
objects[ret].data = new PortData();
|
objects[ret].data = new PortData();
|
||||||
const auto data = static_cast<PortData*>(objects[ret].data);
|
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
|
// If the name is empty (ie the first char is the null terminator) then the port is private
|
||||||
data->isPublic = name[0] != '\0';
|
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);
|
// printf("Created %s port \"%s\" with handle %d\n", data->isPublic ? "public" : "private", data->name, ret);
|
||||||
return 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
|
// Get the handle of a port based on its name
|
||||||
// If there's no such port, return nullopt
|
// If there's no such port, return nullopt
|
||||||
std::optional<Handle> Kernel::getPortHandle(const char* name) {
|
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)
|
// Result ConnectToPort(Handle* out, const char* portName)
|
||||||
void Kernel::connectToPort() {
|
void Kernel::connectToPort() {
|
||||||
const u32 handlePointer = regs[0];
|
const u32 handlePointer = regs[0];
|
||||||
const char* port = static_cast<const char*>(mem.getReadPointer(regs[1]));
|
// Read up to max + 1 characters to see if the name is too long
|
||||||
printf("ConnectToPort(handle pointer = %08X, port = \"%s\")\n", handlePointer, port);
|
std::string port = mem.readString(regs[1], PortData::maxNameLen + 1);
|
||||||
|
|
||||||
// TODO: This is unsafe
|
if (port.size() > PortData::maxNameLen) {
|
||||||
if (std::strlen(port) > PortData::maxNameLen) {
|
|
||||||
Helpers::panic("ConnectToPort: Port name too long\n");
|
Helpers::panic("ConnectToPort: Port name too long\n");
|
||||||
regs[0] = SVCResult::PortNameTooLong;
|
regs[0] = SVCResult::PortNameTooLong;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto portHandle = getPortHandle(port);
|
// Try getting a handle to the port
|
||||||
if (!portHandle.has_value()) [[unlikely]] {
|
std::optional<Handle> optionalHandle = getPortHandle(port.c_str());
|
||||||
|
if (!optionalHandle.has_value()) [[unlikely]] {
|
||||||
Helpers::panic("ConnectToPort: Port doesn't exist\n");
|
Helpers::panic("ConnectToPort: Port doesn't exist\n");
|
||||||
regs[0] = SVCResult::ObjectNotFound;
|
regs[0] = SVCResult::ObjectNotFound;
|
||||||
return;
|
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
|
// TODO: Actually create session
|
||||||
Handle sessionHandle = makeObject(KernelObjectType::Session);
|
Handle sessionHandle = makeSession(portHandle);
|
||||||
|
|
||||||
// TODO: Should the result be written back to [r0]?
|
// TODO: Should the result be written back to [r0]?
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = SVCResult::Success;
|
||||||
|
@ -70,8 +103,14 @@ void Kernel::sendSyncRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto sessionData = static_cast<SessionData*>(session->data);
|
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;
|
regs[0] = SVCResult::Success;
|
||||||
}
|
}
|
|
@ -202,4 +202,20 @@ void* Memory::getWritePointer(u32 address) {
|
||||||
uintptr_t pointer = writeTable[page];
|
uintptr_t pointer = writeTable[page];
|
||||||
if (pointer == 0) return nullptr;
|
if (pointer == 0) return nullptr;
|
||||||
return (void*)(pointer + offset);
|
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;
|
||||||
}
|
}
|
59
src/core/services/service_manager.cpp
Normal file
59
src/core/services/service_manager.cpp
Normal 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";
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue