system service: deliver initial AppContent entitlement updates through shared queue

This commit is contained in:
nmzik 2026-10-08 04:29:49 +04:00
parent 210d4dcd99
commit 62643c3424
4 changed files with 88 additions and 1 deletions

View File

@ -6,6 +6,7 @@
#include "common/stringUtils.h"
#include "libs/errno.h"
#include "libs/libs.h"
#include "libs/systemService.h"
#include "loader/symbolDatabase.h"
#include "loader/systemContent.h"
@ -96,6 +97,7 @@ int KYTY_SYSV_ABI AppContentInitialize(const AppContentInitParam* init_param,
LOGF("\t TITLE_ID missing\n");
}
SystemService::NotifyEntitlementUpdate();
return OK;
}

View File

@ -7,9 +7,12 @@
#include "libs/dialog.h"
#include "libs/errno.h"
#include "libs/libs.h"
#include "libs/systemService.h"
#include "loader/symbolDatabase.h"
#include <cstring>
#include <deque>
#include <mutex>
namespace Libs {
@ -70,6 +73,14 @@ struct SystemServiceEvent {
uint8_t data[8192];
};
static std::mutex g_event_mutex;
static std::deque<int32_t> g_events;
void NotifyEntitlementUpdate() {
std::scoped_lock lock(g_event_mutex);
g_events.push_back(0x10000003); // SCE_SYSTEM_SERVICE_EVENT_ENTITLEMENT_UPDATE
}
struct SystemServiceDisplaySafeAreaInfo {
float ratio;
uint8_t reserved[128];
@ -153,7 +164,14 @@ static int KYTY_SYSV_ABI SystemServiceReceiveEvent(SystemServiceEvent* event) {
event->event_type = -1;
std::memset(event->data, 0, sizeof(event->data));
return SYSTEM_SERVICE_ERROR_NO_EVENT;
std::scoped_lock lock(g_event_mutex);
if (g_events.empty()) {
return SYSTEM_SERVICE_ERROR_NO_EVENT;
}
event->event_type = g_events.front();
g_events.pop_front();
LOGF("\t SystemService event = 0x%08x\n", static_cast<uint32_t>(event->event_type));
return OK;
}
static int KYTY_SYSV_ABI SystemServiceGetStatus(SystemServiceStatus* status) {
@ -167,6 +185,10 @@ static int KYTY_SYSV_ABI SystemServiceGetStatus(SystemServiceStatus* status) {
*status = SystemServiceStatus();
status->is_system_ui_overlaid = dialog.active;
status->is_in_background_execution = dialog.background;
{
std::scoped_lock lock(g_event_mutex);
status->event_num = static_cast<int32_t>(g_events.size());
}
return OK;
}

10
src/libs/systemService.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef EMULATOR_LIBS_SYSTEMSERVICE_H_
#define EMULATOR_LIBS_SYSTEMSERVICE_H_
namespace Libs::SystemService {
void NotifyEntitlementUpdate();
} // namespace Libs::SystemService
#endif

View File

@ -46,6 +46,8 @@ void InitLibKernel_1_Apr(Loader::SymbolDatabase *symbols);
namespace Libs {
void InitLibKernel_1(Loader::SymbolDatabase *symbols);
void InitSysmodule_1(Loader::SymbolDatabase *symbols);
void InitSystemService_1(Loader::SymbolDatabase *symbols);
void InitAppContent_1(Loader::SymbolDatabase *symbols);
}
namespace Libs::LibAmpr {
@ -204,6 +206,56 @@ void TestSysmoduleReferences() {
"internal and public module operations share load state");
}
void TestSystemServiceEntitlementEvents() {
Loader::SymbolDatabase symbols;
Libs::InitSystemService_1(&symbols);
Libs::InitAppContent_1(&symbols);
const auto resolve = [&](const char* nid, const char* library, const char* module) {
const auto* symbol = symbols.Find(
{nid, library, 1, module, 1, 1, Loader::SymbolType::Func});
Check(symbol != nullptr, "entitlement event export resolves");
return symbol->vaddr;
};
const auto initialize = reinterpret_cast<int (KYTY_SYSV_ABI *)(const void*, void*)>(
resolve("R9lA82OraNs", "AppContent", "AppContentUtil"));
struct Status {
int32_t event_num;
bool overlay, background, vr;
uint8_t reserved[127];
};
struct Event {
int32_t type;
uint8_t data[8192];
};
static_assert(sizeof(Status) == 136 && sizeof(Event) == 8196);
const auto get_status = reinterpret_cast<int (KYTY_SYSV_ABI *)(Status*)>(
resolve("rPo6tV8D9bM", "SystemService", "SystemService"));
const auto receive = reinterpret_cast<int (KYTY_SYSV_ABI *)(Event*)>(
resolve("656LMQSrg6U", "SystemService", "SystemService"));
Status status {};
Event event {};
Check(get_status(&status) == OK && status.event_num == 0,
"SystemService starts without pending events");
std::array<uint8_t, 32> init {};
std::array<uint8_t, 40> boot {};
Check(initialize(init.data(), boot.data()) == OK,
"AppContent initialization generates an entitlement notification");
Check(get_status(&status) == OK && status.event_num == 1 &&
get_status(&status) == OK && status.event_num == 1,
"status queries preserve pending notifications");
Check(receive(nullptr) == Libs::SystemService::SYSTEM_SERVICE_ERROR_PARAMETER &&
get_status(&status) == OK && status.event_num == 1,
"invalid event output does not consume a notification");
std::memset(&event, 0xff, sizeof(event));
Check(receive(&event) == OK && event.type == 0x10000003 &&
std::all_of(std::begin(event.data), std::end(event.data),
[](uint8_t byte) { return byte == 0; }) &&
get_status(&status) == OK && status.event_num == 0,
"receive delivers the entitlement event with cleared payload exactly once");
Check(receive(&event) == Libs::SystemService::SYSTEM_SERVICE_ERROR_NO_EVENT,
"empty event queue reports NO_EVENT");
}
void TestSaveOpenVisibility() {
constexpr char Path[] = "/savedata0/visible-save.dat";
constexpr char Payload[] = "saved progress";
@ -1573,6 +1625,7 @@ int main(int, char**) {
TempDirectory temporary;
FileSystem::Initialize();
TestSysmoduleReferences();
TestSystemServiceEntitlementEvents();
TestRandomDevices();
TestFileDescriptorFlags();
CheckMountRoot(temporary.Path());