Implement mmnv::IRequest for media module clock control

This is used by games before calling into nvdec in order to clock up the
HW module, it can also be used to request a RAM frequency. Since we
obviously don't emulate the hardware down to this level a basic stub
that provides the correct reponses is enough.

Fixes a crash on first level of Super Mario Odyssey.
This commit is contained in:
Billy Laws 2021-05-30 18:08:18 +01:00
parent 54d39fbaa7
commit a7dc69223b
4 changed files with 254 additions and 0 deletions

View File

@ -192,6 +192,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/ssl/ISslService.cpp
${source_DIR}/skyline/services/ssl/ISslContext.cpp
${source_DIR}/skyline/services/prepo/IPrepoService.cpp
${source_DIR}/skyline/services/mmnv/IRequest.cpp
)
# target_precompile_headers(skyline PRIVATE ${source_DIR}/skyline/common.h) # PCH will currently break Intellisense
target_link_libraries(skyline android perfetto fmt lz4_static tzcode oboe mbedtls::mbedcrypto)

View File

@ -0,0 +1,146 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KProcess.h>
#include "IRequest.h"
namespace skyline::service::mmnv {
IRequest::IRequest(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
IRequest::Allocation IRequest::AllocateRequest() {
// Search existing requests first
for (u32 i{}; i < requests.size(); i++) {
auto &req = requests[i];
if (!req) {
return {req.emplace(), i};
}
}
// Allocate a new request otherwise
return {requests.emplace_back().emplace(), static_cast<u32>(requests.size() - 1)};
}
Result IRequest::InitializeOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto module{request.Pop<ModuleType>()};
request.Skip<u32>(); // Unknown unused param in HOS
//u32 autoClear{request.Pop<u32>()};
std::lock_guard lock(requestsMutex);
AllocateRequest().request.module = module;
return {};
}
Result IRequest::FinalizeOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto module{request.Pop<ModuleType>()};
std::lock_guard lock(requestsMutex);
for (auto &req : requests) {
if (req && req->module == module) {
req.reset();
break;
}
}
return {};
}
Result IRequest::SetAndWaitOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto module{request.Pop<ModuleType>()};
u32 freqHz{request.Pop<u32>()};
std::lock_guard lock(requestsMutex);
for (auto &req : requests) {
if (req && req->module == module) {
req->freqHz = freqHz;
state.logger->Debug("Set frequency for module {}: {} Hz", static_cast<u32>(module), freqHz);
return {};
}
}
// This doesn't return any errors in HOS
state.logger->Warn("Tried to set frequency to {} Hz for unregistered module {}", freqHz, static_cast<u32>(module));
return {};
}
Result IRequest::GetOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto module{request.Pop<ModuleType>()};
std::lock_guard lock(requestsMutex);
for (auto &req : requests) {
if (req && req->module == module) {
state.logger->Debug("Get frequency for module {}: {} Hz", static_cast<u32>(module), req->freqHz);
response.Push<u32>(req->freqHz);
return {};
}
}
// This doesn't return any errors in HOS
state.logger->Warn("Tried to get frequency of unregistered module {}", static_cast<u32>(module));
response.Push<u32>(0);
return {};
}
Result IRequest::Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto module{request.Pop<ModuleType>()};
request.Skip<u32>(); // Unknown unused param in HOS
//u32 autoClear{request.Pop<u32>()};
std::lock_guard lock(requestsMutex);
auto req{AllocateRequest()};
req.request.module = module;
response.Push<u32>(req.id);
return {};
}
Result IRequest::Finalize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u32 id{request.Pop<u32>()};
std::lock_guard lock(requestsMutex);
if (id >= requests.size())
return {};
requests[id].reset();
return {};
};
Result IRequest::SetAndWait(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u32 id{request.Pop<u32>()};
u32 freqHz{request.Pop<u32>()};
std::lock_guard lock(requestsMutex);
if (id < requests.size()) {
auto &req{requests[id]};
if (req) {
req->freqHz = freqHz;
state.logger->Debug("Set frequency for request {}: {} Hz", id, freqHz);
return {};
}
}
// This doesn't return any errors in HOS
state.logger->Warn("Tried to set frequency for unregistered request {}", id);
return {};
}
Result IRequest::Get(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u32 id{request.Pop<u32>()};
u32 freqHz{request.Pop<u32>()};
std::lock_guard lock(requestsMutex);
if (id >= requests.size()) {
auto &req{requests[id]};
if (req) {
state.logger->Debug("Get frequency for request {}: {} Hz", id, req->freqHz);
response.Push<u32>(req->freqHz);
return {};
}
}
// This doesn't return any errors in HOS
state.logger->Warn("Tried to get frequency of unregistered request {}", id);
response.Push<u32>(0);
return {};
}
}

View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <kernel/types/KEvent.h>
#include <services/serviceman.h>
namespace skyline::service::mmnv {
/**
* @brief IRequest or mm:u is used to control clocks and powergating of various hardware modules
* @url https://switchbrew.org/wiki/Display_services#mm:u
*/
class IRequest : public BaseService {
private:
/**
* @brief Enumerates the modules that can be controlled by mmnv, these are passed directly to FGM services
*/
enum class ModuleType : u32 {
Ram = 2,
NvEnc = 5,
NvDec = 6,
NvJpg = 7
};
/**
* @brief Holds a single mmnv request, detailing its target module and current frequency
*/
struct Request {
ModuleType module;
u32 freqHz;
};
std::mutex requestsMutex; // Protects accesses to requests
std::vector<std::optional<Request>> requests; //!< Holds allocated requests with the index corresponding to the request ID
/**
* @brief Holds the result of a request allocation
*/
struct Allocation {
Request &request;
u32 id;
};
/*
* @note requestsMutex should be locked when calling this
* @return A reference to an empty request
*/
Allocation AllocateRequest();
public:
IRequest(const DeviceState &state, ServiceManager &manager);
/**
* @brief Initialises the request for the given module ID
*/
Result InitializeOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Finalises the request for the given module ID
*/
Result FinalizeOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Sets the target frequency in HZ for the given module and waits for it to be applied
*/
Result SetAndWaitOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Gets the frequency in HZ for the given module
*/
Result GetOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Initialises a new request for the given module ID and returns a new request ID
*/
Result Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Finalises the request with the given ID
*/
Result Finalize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Sets the target frequency in HZ for the request with the given ID and waits for it to be applied
*/
Result SetAndWait(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Gets the frequency in HZ for the request with the given ID
*/
Result Get(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL(
SFUNC(0x0, IRequest, InitializeOld),
SFUNC(0x1, IRequest, FinalizeOld),
SFUNC(0x2, IRequest, SetAndWaitOld),
SFUNC(0x3, IRequest, GetOld),
SFUNC(0x4, IRequest, Initialize),
SFUNC(0x5, IRequest, Finalize),
SFUNC(0x6, IRequest, SetAndWait),
SFUNC(0x7, IRequest, Get)
)
};
}

View File

@ -30,6 +30,7 @@
#include "socket/bsd/IClient.h"
#include "ssl/ISslService.h"
#include "prepo/IPrepoService.h"
#include "mmnv/IRequest.h"
#include "serviceman.h"
#define SERVICE_CASE(class, name, ...) \
@ -90,6 +91,7 @@ namespace skyline::service {
SERVICE_CASE(socket::IClient, "bsd:u")
SERVICE_CASE(ssl::ISslService, "ssl")
SERVICE_CASE(prepo::IPrepoService, "prepo:u")
SERVICE_CASE(mmnv::IRequest, "mm:u")
default:
std::string_view nameString(span(reinterpret_cast<char *>(&name), sizeof(name)).as_string(true));
throw std::out_of_range(fmt::format("CreateService called with an unknown service name: {}", nameString));