#include "services/service_manager.hpp" #include #include "ipc.hpp" #include "kernel.hpp" ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem), csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16; // Reset every single service void ServiceManager::reset() { ac.reset(); act.reset(); am.reset(); apt.reset(); boss.reset(); cam.reset(); cecd.reset(); cfg.reset(); csnd.reset(); dlp_srvr.reset(); dsp.reset(); hid.reset(); http.reset(); ir_user.reset(); frd.reset(); fs.reset(); gsp_gpu.reset(); gsp_lcd.reset(); ldr.reset(); mcu_hwc.reset(); mic.reset(); ndm.reset(); news_u.reset(); nfc.reset(); nim.reset(); ptm.reset(); soc.reset(); ssl.reset(); y2r.reset(); notificationSemaphore = std::nullopt; } // 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 // messagePointer: The base pointer for the IPC message void ServiceManager::handleSyncRequest(u32 messagePointer) { const u32 header = mem.read32(messagePointer); switch (header) { case Commands::EnableNotification: enableNotification(messagePointer); break; case Commands::ReceiveNotification: receiveNotification(messagePointer); break; case Commands::RegisterClient: registerClient(messagePointer); break; case Commands::GetServiceHandle: getServiceHandle(messagePointer); break; case Commands::Subscribe: subscribe(messagePointer); break; case Commands::Unsubscribe: unsubscribe(messagePointer); break; default: Helpers::panic("Unknown \"srv:\" command: %08X", header); } } // https://www.3dbrew.org/wiki/SRV:RegisterClient void ServiceManager::registerClient(u32 messagePointer) { log("srv::registerClient (Stubbed)\n"); mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } // clang-format off static std::map serviceMap = { { "ac:u", KernelHandles::AC }, { "act:a", KernelHandles::ACT }, { "act:u", KernelHandles::ACT }, { "am:app", KernelHandles::AM }, { "APT:S", KernelHandles::APT }, // TODO: APT:A, APT:S and APT:U are slightly different { "APT:A", KernelHandles::APT }, { "APT:U", KernelHandles::APT }, { "boss:U", KernelHandles::BOSS }, { "cam:u", KernelHandles::CAM }, { "cecd:u", KernelHandles::CECD }, { "cfg:u", KernelHandles::CFG_U }, { "cfg:i", KernelHandles::CFG_I }, { "cfg:s", KernelHandles::CFG_S }, { "csnd:SND", KernelHandles::CSND }, { "dlp:SRVR", KernelHandles::DLP_SRVR }, { "dsp::DSP", KernelHandles::DSP }, { "hid:USER", KernelHandles::HID }, { "http:C", KernelHandles::HTTP }, { "ir:USER", KernelHandles::IR_USER }, { "frd:a", KernelHandles::FRD_A }, { "frd:u", KernelHandles::FRD_U }, { "fs:USER", KernelHandles::FS }, { "gsp::Gpu", KernelHandles::GPU }, { "gsp::Lcd", KernelHandles::LCD }, { "ldr:ro", KernelHandles::LDR_RO }, { "mcu::HWC", KernelHandles::MCU_HWC }, { "mic:u", KernelHandles::MIC }, { "ndm:u", KernelHandles::NDM }, { "news:u", KernelHandles::NEWS_U }, { "nfc:u", KernelHandles::NFC }, { "ns:s", KernelHandles::NS_S }, { "nwm::UDS", KernelHandles::NWM_UDS }, { "nim:aoc", KernelHandles::NIM }, { "ptm:u", KernelHandles::PTM_U }, // TODO: ptm:u and ptm:sysm have very different command sets { "ptm:sysm", KernelHandles::PTM_SYSM }, { "ptm:play", KernelHandles::PTM_PLAY }, { "soc:U", KernelHandles::SOC }, { "ssl:C", KernelHandles::SSL }, { "y2r:u", KernelHandles::Y2R }, }; // clang-format on // https://www.3dbrew.org/wiki/SRV:GetServiceHandle void ServiceManager::getServiceHandle(u32 messagePointer) { u32 nameLength = mem.read32(messagePointer + 12); u32 flags = mem.read32(messagePointer + 16); u32 handle = 0; std::string service = mem.readString(messagePointer + 4, 8); log("srv::getServiceHandle (Service: %s, nameLength: %d, flags: %d)\n", service.c_str(), nameLength, flags); // Look up service handle in map, panic if it does not exist if (auto search = serviceMap.find(service); search != serviceMap.end()) handle = search->second; else Helpers::panic("srv: GetServiceHandle with unknown service %s", service.c_str()); mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 2)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 12, handle); } void ServiceManager::enableNotification(u32 messagePointer) { log("srv::EnableNotification()\n"); // Make a semaphore for notifications if none exists currently if (!notificationSemaphore.has_value() || kernel.getObject(notificationSemaphore.value(), KernelObjectType::Semaphore) == nullptr) { notificationSemaphore = kernel.makeSemaphore(0, MAX_NOTIFICATION_COUNT); } mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 2)); mem.write32(messagePointer + 4, Result::Success); // Result code mem.write32(messagePointer + 8, 0); // Translation descriptor // Handle to semaphore signaled on process notification mem.write32(messagePointer + 12, notificationSemaphore.value()); } void ServiceManager::receiveNotification(u32 messagePointer) { log("srv::ReceiveNotification() (STUBBED)\n"); mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0)); mem.write32(messagePointer + 4, Result::Success); // Result code mem.write32(messagePointer + 8, 0); // Notification ID } void ServiceManager::subscribe(u32 messagePointer) { u32 id = mem.read32(messagePointer + 4); log("srv::Subscribe (id = %d) (stubbed)\n", id); mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } void ServiceManager::unsubscribe(u32 messagePointer) { u32 id = mem.read32(messagePointer + 4); log("srv::Unsubscribe (id = %d) (stubbed)\n", id); mem.write32(messagePointer, IPC::responseHeader(0xA, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { switch (handle) { // Breaking alphabetical order a bit to place the ones I think are most common at the top case KernelHandles::GPU: [[likely]] gsp_gpu.handleSyncRequest(messagePointer); break; case KernelHandles::FS: [[likely]] fs.handleSyncRequest(messagePointer); break; case KernelHandles::APT: [[likely]] apt.handleSyncRequest(messagePointer); break; case KernelHandles::DSP: [[likely]] dsp.handleSyncRequest(messagePointer); break; case KernelHandles::AC: ac.handleSyncRequest(messagePointer); break; case KernelHandles::ACT: act.handleSyncRequest(messagePointer); break; case KernelHandles::AM: am.handleSyncRequest(messagePointer); break; case KernelHandles::BOSS: boss.handleSyncRequest(messagePointer); break; case KernelHandles::CAM: cam.handleSyncRequest(messagePointer); break; case KernelHandles::CECD: cecd.handleSyncRequest(messagePointer); break; case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer, CFGService::Type::U); break; case KernelHandles::CFG_I: cfg.handleSyncRequest(messagePointer, CFGService::Type::I); break; case KernelHandles::CFG_S: cfg.handleSyncRequest(messagePointer, CFGService::Type::S); break; case KernelHandles::CSND: csnd.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; case KernelHandles::IR_USER: ir_user.handleSyncRequest(messagePointer); break; case KernelHandles::FRD_A: frd.handleSyncRequest(messagePointer, FRDService::Type::A); break; case KernelHandles::FRD_U: frd.handleSyncRequest(messagePointer, FRDService::Type::U); break; case KernelHandles::LCD: gsp_lcd.handleSyncRequest(messagePointer); break; case KernelHandles::LDR_RO: ldr.handleSyncRequest(messagePointer); break; case KernelHandles::MCU_HWC: mcu_hwc.handleSyncRequest(messagePointer); break; case KernelHandles::MIC: mic.handleSyncRequest(messagePointer); break; case KernelHandles::NFC: nfc.handleSyncRequest(messagePointer); break; case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break; case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break; case KernelHandles::NEWS_U: news_u.handleSyncRequest(messagePointer); break; case KernelHandles::NS_S: Helpers::panic("Unimplemented SendSyncRequest to ns:s"); break; case KernelHandles::NWM_UDS: nwm_uds.handleSyncRequest(messagePointer); break; case KernelHandles::PTM_PLAY: ptm.handleSyncRequest(messagePointer, PTMService::Type::PLAY); break; case KernelHandles::PTM_SYSM: ptm.handleSyncRequest(messagePointer, PTMService::Type::SYSM); break; case KernelHandles::PTM_U: ptm.handleSyncRequest(messagePointer, PTMService::Type::U); break; case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break; case KernelHandles::SSL: ssl.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)); } }