diff --git a/.github/workflows/Linux_Build.yml b/.github/workflows/Linux_Build.yml index e141b465..bbc79d81 100644 --- a/.github/workflows/Linux_Build.yml +++ b/.github/workflows/Linux_Build.yml @@ -24,7 +24,7 @@ jobs: run: git submodule update --init --recursive - name: Install misc packages - run: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx + run: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@v1.2.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 64b11838..986bcc27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,7 +135,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services src/core/services/frd.cpp src/core/services/nim.cpp src/core/services/shared_font.cpp src/core/services/y2r.cpp src/core/services/cam.cpp src/core/services/ldr_ro.cpp src/core/services/act.cpp src/core/services/nfc.cpp src/core/services/dlp_srvr.cpp - src/core/services/ir_user.cpp src/core/services/http.cpp + src/core/services/ir_user.cpp src/core/services/http.cpp src/core/services/soc.cpp ) set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp src/core/PICA/shader_interpreter.cpp src/core/PICA/dynapica/shader_rec.cpp @@ -176,7 +176,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/config.hpp include/services/ir_user.hpp include/http_server.hpp include/cheats.hpp include/action_replay.hpp include/renderer_sw/renderer_sw.hpp include/compiler_builtins.hpp include/fs/romfs.hpp include/fs/ivfc.hpp include/discord_rpc.hpp include/services/http.hpp include/result/result_cfg.hpp - include/math_util.hpp + include/math_util.hpp include/services/soc.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp diff --git a/include/kernel/config_mem.hpp b/include/kernel/config_mem.hpp index 062eb551..71f46390 100644 --- a/include/kernel/config_mem.hpp +++ b/include/kernel/config_mem.hpp @@ -9,6 +9,13 @@ namespace ConfigMem { SyscoreVer = 0x1FF80010, EnvInfo = 0x1FF80014, AppMemAlloc = 0x1FF80040, + FirmUnknown = 0x1FF80060, + FirmRevision = 0x1FF80061, + FirmVersionMinor = 0x1FF80062, + FirmVersionMajor = 0x1FF80063, + FirmSyscoreVer = 0x1FF80064, + FirmSdkVer = 0x1FF80068, + HardwareType = 0x1FF81004, Datetime0 = 0x1FF81020, WifiMac = 0x1FF81060, diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index faccf148..256ae02b 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -17,7 +17,8 @@ namespace KernelHandles { BOSS, // Streetpass stuff? CAM, // Camera service CECD, // More Streetpass stuff? - CFG, // CFG service (Console & region info) + CFG_U, // CFG service (Console & region info) + CFG_I, DLP_SRVR, // Download Play: Server. Used for network play. DSP, // DSP service (Used for audio decoding and output) HID, // HID service (Handles input-related things including gyro. Does NOT handle New3DS controls or CirclePadPro) @@ -33,6 +34,7 @@ namespace KernelHandles { NIM, // Updates, DLC, etc NDM, // ????? PTM, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state) + SOC, // Socket service Y2R, // Also does camera stuff MinServiceHandle = AC, @@ -66,7 +68,8 @@ namespace KernelHandles { case BOSS: return "BOSS"; case CAM: return "CAM"; case CECD: return "CECD"; - case CFG: return "CFG"; + case CFG_U: return "CFG:U"; + case CFG_I: return "CFG:I"; case DSP: return "DSP"; case DLP_SRVR: return "DLP::SRVR"; case HID: return "HID"; @@ -82,6 +85,7 @@ namespace KernelHandles { case NFC: return "NFC"; case NIM: return "NIM"; case PTM: return "PTM"; + case SOC: return "SOC"; case Y2R: return "Y2R"; default: return "Unknown"; } diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 06f5dceb..e1d521c7 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -34,6 +34,7 @@ class Kernel { std::vector objects; std::vector portHandles; + std::vector mutexHandles; // Thread indices, sorted by priority std::vector threadIndices; diff --git a/include/logger.hpp b/include/logger.hpp index a3112dde..26ecb70f 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -53,6 +53,7 @@ namespace Log { static Logger nimLogger; static Logger ndmLogger; static Logger ptmLogger; + static Logger socLogger; static Logger y2rLogger; static Logger srvLogger; diff --git a/include/memory.hpp b/include/memory.hpp index 0b3b184c..89d191b3 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -139,6 +139,19 @@ private: // Report a retail unit without JTAG static constexpr u32 envInfo = 1; + // Stored in Configuration Memory starting @ 0x1FF80060 + struct FirmwareInfo { + u8 unk; // Usually 0 according to 3DBrew + u8 revision; + u8 minor; + u8 major; + u32 syscoreVer; + u32 sdkVer; + }; + + // Values taken from 3DBrew and Citra + static constexpr FirmwareInfo firm{.unk = 0, .revision = 0, .minor = 0x34, .major = 2, .syscoreVer = 2, .sdkVer = 0x0000F297}; + public: u16 kernelVersion = 0; u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore) diff --git a/include/services/cfg.hpp b/include/services/cfg.hpp index 666e8b23..c8c8adde 100644 --- a/include/services/cfg.hpp +++ b/include/services/cfg.hpp @@ -7,7 +7,6 @@ #include "result/result.hpp" class CFGService { - Handle handle = KernelHandles::CFG; Memory& mem; CountryCodes country = CountryCodes::US; // Default to USA MAKE_LOG_FUNCTION(log, cfgLogger) diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 9e1d9b2f..08460cd5 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -29,6 +29,7 @@ #include "services/nfc.hpp" #include "services/nim.hpp" #include "services/ptm.hpp" +#include "services/soc.hpp" #include "services/y2r.hpp" // More circular dependencies!! @@ -66,6 +67,7 @@ class ServiceManager { NIMService nim; NDMService ndm; PTMService ptm; + SOCService soc; Y2RService y2r; // "srv:" commands diff --git a/include/services/soc.hpp b/include/services/soc.hpp new file mode 100644 index 00000000..88f0b456 --- /dev/null +++ b/include/services/soc.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +class SOCService { + Handle handle = KernelHandles::SOC; + Memory& mem; + MAKE_LOG_FUNCTION(log, socLogger) + + bool initialized = false; + + // Service commands + void initializeSockets(u32 messagePointer); + +public: + SOCService(Memory& mem) : mem(mem) {} + void reset(); + void handleSyncRequest(u32 messagePointer); +}; \ No newline at end of file diff --git a/src/core/fs/ivfc.cpp b/src/core/fs/ivfc.cpp index 78e50f29..ca8c5e26 100644 --- a/src/core/fs/ivfc.cpp +++ b/src/core/fs/ivfc.cpp @@ -58,7 +58,7 @@ namespace IVFC { // According to 3DBrew, this is usually the case but not guaranteed if (ivfcActualSize != ivfcDescriptorSize) { - printf("IVFC descriptor size mismatch: %lx != %lx\n", ivfcActualSize, ivfcDescriptorSize); + printf("IVFC descriptor size mismatch: %llx != %llx\n", ivfcActualSize, ivfcDescriptorSize); } if (magicIdentifier == 0x10000 && ivfcActualSize != 0x5C) { @@ -73,4 +73,4 @@ namespace IVFC { return ivfcActualSize; } -} // namespace IVFC \ No newline at end of file +} // namespace IVFC diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index e7b34923..6f3cf081 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -6,6 +6,7 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu) : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this) { objects.reserve(512); // Make room for a few objects to avoid further memory allocs later + mutexHandles.reserve(8); portHandles.reserve(32); threadIndices.reserve(appResourceLimits.maxThreads); @@ -139,6 +140,7 @@ void Kernel::reset() { deleteObjectData(object); } objects.clear(); + mutexHandles.clear(); portHandles.clear(); threadIndices.clear(); serviceManager.reset(); @@ -256,6 +258,7 @@ void Kernel::duplicateHandle() { namespace SystemInfoType { enum : u32 { + MemoryInformation = 0, // Gets information related to Citra (We don't implement this, we just report this emulator is not Citra) CitraInformation = 0x20000, // Gets information related to this emulator @@ -292,6 +295,22 @@ void Kernel::getSystemInfo() { regs[0] = Result::Success; switch (infoType) { + case SystemInfoType::MemoryInformation: { + switch (subtype) { + // Total used memory size in the APPLICATION memory region + case 1: + regs[1] = mem.getUsedUserMem(); + regs[2] = 0; + break; + + default: + Helpers::panic("GetSystemInfo: Unknown MemoryInformation subtype %x\n", subtype); + regs[0] = Result::FailurePlaceholder; + break; + } + break; + } + case SystemInfoType::CitraInformation: { switch (subtype) { case CitraInfoType::IsCitra: diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 2e39b620..440045d3 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -168,6 +168,11 @@ Handle Kernel::makeMutex(bool locked) { moo->ownerThread = currentThreadIndex; } + // Push the new mutex to our list of mutex handles + // We need a list of mutex handles so that when a thread is killed, we can look which mutexes from this list the thread owns and free them + // Alternatively this could be a per-thread list, but I don't want to push_back and remove on every mutex lock and release + // Since some mutexes like the APT service mutex are locked and unlocked constantly, while ExitThread is a relatively "rare" SVC + mutexHandles.push_back(ret); return ret; } @@ -466,6 +471,23 @@ void Kernel::setThreadPriority() { void Kernel::exitThread() { logSVC("ExitThread\n"); + // Find which mutexes this thread owns, release them + for (auto handle : mutexHandles) { + KernelObject* object = getObject(handle, KernelObjectType::Mutex); + + // Make sure that the handle actually matches to a mutex, and if our exiting thread owns the mutex, release it + if (object != nullptr) { + Mutex* moo = object->getData(); + + if (moo->locked && moo->ownerThread == currentThreadIndex) { + // Release the mutex by setting lock count to 1 and releasing it once. We set lock count to 1 since it's a recursive mutex + // Therefore if its lock count was > 1, simply calling releaseMutex would not fully release it + moo->lockCount = 1; + releaseMutex(moo); + } + } + } + // Remove the index of this thread from the thread indices vector for (int i = 0; i < threadIndices.size(); i++) { if (threadIndices[i] == currentThreadIndex) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3b987507..d37f2eea 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -91,6 +91,12 @@ u8 Memory::read8(u32 vaddr) { case ConfigMem::NetworkState: return 2; // Report that we've got an internet connection case ConfigMem::HeadphonesConnectedMaybe: return 0; case ConfigMem::Unknown1086: return 1; // It's unknown what this is but some games want it to be 1 + + case ConfigMem::FirmUnknown: return firm.unk; + case ConfigMem::FirmRevision: return firm.revision; + case ConfigMem::FirmVersionMinor: return firm.minor; + case ConfigMem::FirmVersionMajor: return firm.major; + default: Helpers::panic("Unimplemented 8-bit read, addr: %08X", vaddr); } } @@ -138,6 +144,8 @@ u32 Memory::read32(u32 vaddr) { // 3D slider. Float in range 0.0 = off, 1.0 = max. case ConfigMem::SliderState3D: return Helpers::bit_cast(0.0f); + case ConfigMem::FirmUnknown: + return u32(read8(vaddr)) | (u32(read8(vaddr + 1)) << 8) | (u32(read8(vaddr + 2)) << 16) | (u32(read8(vaddr + 3)) << 24); default: if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) { diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 124e5c95..4ad7f353 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -2,6 +2,8 @@ #include "ipc.hpp" #include "kernel.hpp" +#include + namespace APTCommands { enum : u32 { GetLockHandle = 0x00010040, @@ -94,10 +96,24 @@ void APTService::appletUtility(u32 messagePointer) { u32 outputSize = mem.read32(messagePointer + 12); u32 inputPointer = mem.read32(messagePointer + 20); - log("APT::AppletUtility(utility = %d, input size = %x, output size = %x, inputPointer = %08X) (Stubbed)\n", utility, inputSize, - outputSize, inputPointer); + log("APT::AppletUtility(utility = %d, input size = %x, output size = %x, inputPointer = %08X) (Stubbed)\n", utility, inputSize, outputSize, + inputPointer); + + std::vector out(outputSize); + const u32 outputBuffer = mem.read32(messagePointer + 0x104); + + if (outputSize >= 1 && utility == 6) { + // TryLockTransition expects a bool indicating success in the output buffer. Set it to true to avoid games panicking (Thanks to Citra) + out[0] = true; + } + mem.write32(messagePointer, IPC::responseHeader(0x4B, 2, 2)); mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, Result::Success); + + for (u32 i = 0; i < outputSize; i++) { + mem.write8(outputBuffer + i, out[i]); + } } void APTService::preloadLibraryApplet(u32 messagePointer) { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 7b64b234..c5b69c0e 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -499,6 +499,12 @@ void FSService::controlArchive(u32 messagePointer) { case 0: // Commit save data changes. Shouldn't need us to do anything mem.write32(messagePointer + 4, Result::Success); break; + + case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment + Helpers::warn("FS::ControlArchive: Tried to retrieve a file's last-modified timestamp"); + mem.write32(messagePointer + 4, Result::Success); + break; + default: Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action); break; diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 20beac6d..56ae6637 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -8,7 +8,8 @@ ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem), cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel), - gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), ptm(mem), y2r(mem, kernel) {} + gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), ptm(mem), soc(mem), + y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16; @@ -36,6 +37,7 @@ void ServiceManager::reset() { nim.reset(); ndm.reset(); ptm.reset(); + soc.reset(); y2r.reset(); notificationSemaphore = std::nullopt; @@ -95,7 +97,8 @@ static std::map serviceMap = { { "boss:U", KernelHandles::BOSS }, { "cam:u", KernelHandles::CAM }, { "cecd:u", KernelHandles::CECD }, - { "cfg:u", KernelHandles::CFG }, + { "cfg:u", KernelHandles::CFG_U }, + { "cfg:i", KernelHandles::CFG_I }, { "dlp:SRVR", KernelHandles::DLP_SRVR }, { "dsp::DSP", KernelHandles::DSP }, { "hid:USER", KernelHandles::HID }, @@ -112,6 +115,7 @@ static std::map serviceMap = { { "nim:aoc", KernelHandles::NIM }, { "ptm:u", KernelHandles::PTM }, // TODO: ptm:u and ptm:sysm have very different command sets { "ptm:sysm", KernelHandles::PTM }, + { "soc:U", KernelHandles::SOC }, { "y2r:u", KernelHandles::Y2R } }; // clang-format on @@ -181,7 +185,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { case KernelHandles::BOSS: boss.handleSyncRequest(messagePointer); break; case KernelHandles::CAM: cam.handleSyncRequest(messagePointer); break; case KernelHandles::CECD: cecd.handleSyncRequest(messagePointer); break; - case KernelHandles::CFG: cfg.handleSyncRequest(messagePointer); break; + case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer); break; case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break; case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break; case KernelHandles::HTTP: http.handleSyncRequest(messagePointer); break; @@ -194,6 +198,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break; case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break; case KernelHandles::PTM: ptm.handleSyncRequest(messagePointer); break; + case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break; case KernelHandles::Y2R: y2r.handleSyncRequest(messagePointer); break; default: Helpers::panic("Sent IPC message to unknown service %08X\n Command: %08X", handle, mem.read32(messagePointer)); } diff --git a/src/core/services/soc.cpp b/src/core/services/soc.cpp new file mode 100644 index 00000000..4ad546b0 --- /dev/null +++ b/src/core/services/soc.cpp @@ -0,0 +1,33 @@ +#include "services/soc.hpp" + +#include "ipc.hpp" +#include "result/result.hpp" + +namespace SOCCommands { + enum : u32 { + InitializeSockets = 0x00010044, + }; +} + +void SOCService::reset() { initialized = false; } + +void SOCService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + switch (command) { + case SOCCommands::InitializeSockets: initializeSockets(messagePointer); break; + default: Helpers::panic("SOC service requested. Command: %08X\n", command); + } +} + +void SOCService::initializeSockets(u32 messagePointer) { + const u32 memoryBlockSize = mem.read32(messagePointer + 4); + const Handle sharedMemHandle = mem.read32(messagePointer + 20); + log("SOC::InitializeSockets (memory block size = %08X, shared mem handle = %08X)\n", memoryBlockSize, sharedMemHandle); + + // TODO: Does double initialization return an error code? + // TODO: Implement the rest of this stuff when it's time to do online. Also implement error checking for the size, shared mem handle, and so on + initialized = true; + + mem.write32(messagePointer, IPC::responseHeader(0x01, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} \ No newline at end of file