early-access version 3024
This commit is contained in:
parent
4b363be254
commit
b7ceb92a69
16 changed files with 573 additions and 56 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 3023.
|
This is the source code for early-access 3024.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ public:
|
||||||
LOG_ERROR(Core_ARM,
|
LOG_ERROR(Core_ARM,
|
||||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||||
num_instructions, memory.Read32(pc));
|
num_instructions, memory.Read32(pc));
|
||||||
|
ReturnException(pc, ARM_Interface::no_execute);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
|
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
|
||||||
|
|
|
@ -152,7 +152,8 @@ public:
|
||||||
Kernel::LimitableResource::Sessions, 1);
|
Kernel::LimitableResource::Sessions, 1);
|
||||||
|
|
||||||
auto* session = Kernel::KSession::Create(kernel);
|
auto* session = Kernel::KSession::Create(kernel);
|
||||||
session->Initialize(nullptr, iface->GetServiceName());
|
session->Initialize(nullptr, iface->GetServiceName(),
|
||||||
|
std::make_shared<Kernel::SessionRequestManager>(kernel));
|
||||||
|
|
||||||
context->AddMoveObject(&session->GetClientSession());
|
context->AddMoveObject(&session->GetClientSession());
|
||||||
iface->ClientConnected(&session->GetServerSession());
|
iface->ClientConnected(&session->GetServerSession());
|
||||||
|
|
|
@ -21,10 +21,9 @@ void KClientSession::Destroy() {
|
||||||
|
|
||||||
void KClientSession::OnServerClosed() {}
|
void KClientSession::OnServerClosed() {}
|
||||||
|
|
||||||
Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
Result KClientSession::SendSyncRequest() {
|
||||||
Core::Timing::CoreTiming& core_timing) {
|
|
||||||
// Signal the server session that new data is available
|
// Signal the server session that new data is available
|
||||||
return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing);
|
return parent->GetServerSession().OnRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -46,8 +46,7 @@ public:
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
Result SendSyncRequest();
|
||||||
Core::Timing::CoreTiming& core_timing);
|
|
||||||
|
|
||||||
void OnServerClosed();
|
void OnServerClosed();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
|
@ -18,13 +20,19 @@
|
||||||
#include "core/hle/kernel/k_server_session.h"
|
#include "core/hle/kernel/k_server_session.h"
|
||||||
#include "core/hle/kernel/k_session.h"
|
#include "core/hle/kernel/k_session.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
|
#include "core/hle/kernel/k_thread_queue.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/service_thread.h"
|
#include "core/hle/kernel/service_thread.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
|
||||||
|
|
||||||
|
static constexpr u32 MessageBufferSize = 0x100;
|
||||||
|
|
||||||
|
KServerSession::KServerSession(KernelCore& kernel_)
|
||||||
|
: KSynchronizationObject{kernel_}, m_lock{kernel_} {}
|
||||||
|
|
||||||
KServerSession::~KServerSession() = default;
|
KServerSession::~KServerSession() = default;
|
||||||
|
|
||||||
|
@ -33,17 +41,14 @@ void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
|
||||||
// Set member variables.
|
// Set member variables.
|
||||||
parent = parent_session_;
|
parent = parent_session_;
|
||||||
name = std::move(name_);
|
name = std::move(name_);
|
||||||
|
|
||||||
if (manager_) {
|
|
||||||
manager = manager_;
|
manager = manager_;
|
||||||
} else {
|
|
||||||
manager = std::make_shared<SessionRequestManager>(kernel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KServerSession::Destroy() {
|
void KServerSession::Destroy() {
|
||||||
parent->OnServerClosed();
|
parent->OnServerClosed();
|
||||||
|
|
||||||
|
this->CleanupRequests();
|
||||||
|
|
||||||
parent->Close();
|
parent->Close();
|
||||||
|
|
||||||
// Release host emulation members.
|
// Release host emulation members.
|
||||||
|
@ -54,13 +59,13 @@ void KServerSession::Destroy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void KServerSession::OnClientClosed() {
|
void KServerSession::OnClientClosed() {
|
||||||
if (manager->HasSessionHandler()) {
|
if (manager && manager->HasSessionHandler()) {
|
||||||
manager->SessionHandler().ClientDisconnected(this);
|
manager->SessionHandler().ClientDisconnected(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KServerSession::IsSignaled() const {
|
bool KServerSession::IsSignaled() const {
|
||||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
|
||||||
|
|
||||||
// If the client is closed, we're always signaled.
|
// If the client is closed, we're always signaled.
|
||||||
if (parent->IsClientClosed()) {
|
if (parent->IsClientClosed()) {
|
||||||
|
@ -68,7 +73,7 @@ bool KServerSession::IsSignaled() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we're signaled if we have a request and aren't handling one.
|
// Otherwise, we're signaled if we have a request and aren't handling one.
|
||||||
return false;
|
return !m_thread_request_list.empty() && m_current_thread_request == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) {
|
void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) {
|
||||||
|
@ -173,9 +178,221 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
Result KServerSession::OnRequest() {
|
||||||
Core::Timing::CoreTiming& core_timing) {
|
// Create the wait queue.
|
||||||
return QueueSyncRequest(thread, memory);
|
ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
|
||||||
|
|
||||||
|
{
|
||||||
|
// Lock the scheduler.
|
||||||
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
|
// Ensure that we can handle new requests.
|
||||||
|
R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed);
|
||||||
|
|
||||||
|
// Check that we're not terminating.
|
||||||
|
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
|
||||||
|
|
||||||
|
if (manager) {
|
||||||
|
// HLE request.
|
||||||
|
auto& memory{kernel.System().Memory()};
|
||||||
|
this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
|
||||||
|
} else {
|
||||||
|
// Non-HLE request.
|
||||||
|
auto* thread{GetCurrentThreadPointer(kernel)};
|
||||||
|
|
||||||
|
// Get whether we're empty.
|
||||||
|
const bool was_empty = m_thread_request_list.empty();
|
||||||
|
|
||||||
|
// Add the thread to the list.
|
||||||
|
thread->Open();
|
||||||
|
m_thread_request_list.push_back(thread);
|
||||||
|
|
||||||
|
// If we were empty, signal.
|
||||||
|
if (was_empty) {
|
||||||
|
this->NotifyAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a synchronous request, so we should wait for our request to complete.
|
||||||
|
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||||
|
GetCurrentThread(kernel).BeginWait(&wait_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetCurrentThread(kernel).GetWaitResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KServerSession::SendReply() {
|
||||||
|
// Lock the session.
|
||||||
|
KScopedLightLock lk(m_lock);
|
||||||
|
|
||||||
|
// Get the request.
|
||||||
|
KThread* client_thread;
|
||||||
|
{
|
||||||
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
|
// Get the current request.
|
||||||
|
client_thread = m_current_thread_request;
|
||||||
|
R_UNLESS(client_thread != nullptr, ResultInvalidState);
|
||||||
|
|
||||||
|
// Clear the current request, since we're processing it.
|
||||||
|
m_current_thread_request = nullptr;
|
||||||
|
if (!m_thread_request_list.empty()) {
|
||||||
|
this->NotifyAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close reference to the request once we're done processing it.
|
||||||
|
SCOPE_EXIT({ client_thread->Close(); });
|
||||||
|
|
||||||
|
// Extract relevant information from the request.
|
||||||
|
// const uintptr_t client_message = request->GetAddress();
|
||||||
|
// const size_t client_buffer_size = request->GetSize();
|
||||||
|
// KThread *client_thread = request->GetThread();
|
||||||
|
// KEvent *event = request->GetEvent();
|
||||||
|
|
||||||
|
// Check whether we're closed.
|
||||||
|
const bool closed = (client_thread == nullptr || parent->IsClientClosed());
|
||||||
|
|
||||||
|
Result result = ResultSuccess;
|
||||||
|
if (!closed) {
|
||||||
|
// If we're not closed, send the reply.
|
||||||
|
Core::Memory::Memory& memory{kernel.System().Memory()};
|
||||||
|
KThread* server_thread{GetCurrentThreadPointer(kernel)};
|
||||||
|
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
||||||
|
|
||||||
|
auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
||||||
|
auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
|
||||||
|
std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
|
||||||
|
} else {
|
||||||
|
result = ResultSessionClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select a result for the client.
|
||||||
|
Result client_result = result;
|
||||||
|
if (closed && R_SUCCEEDED(result)) {
|
||||||
|
result = ResultSessionClosed;
|
||||||
|
client_result = ResultSessionClosed;
|
||||||
|
} else {
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's a client thread, update it.
|
||||||
|
if (client_thread != nullptr) {
|
||||||
|
// End the client thread's wait.
|
||||||
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
|
if (!client_thread->IsTerminationRequested()) {
|
||||||
|
client_thread->EndWait(client_result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KServerSession::ReceiveRequest() {
|
||||||
|
// Lock the session.
|
||||||
|
KScopedLightLock lk(m_lock);
|
||||||
|
|
||||||
|
// Get the request and client thread.
|
||||||
|
// KSessionRequest *request;
|
||||||
|
KThread* client_thread;
|
||||||
|
|
||||||
|
{
|
||||||
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
|
// Ensure that we can service the request.
|
||||||
|
R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
|
||||||
|
|
||||||
|
// Ensure we aren't already servicing a request.
|
||||||
|
R_UNLESS(m_current_thread_request == nullptr, ResultNotFound);
|
||||||
|
|
||||||
|
// Ensure we have a request to service.
|
||||||
|
R_UNLESS(!m_thread_request_list.empty(), ResultNotFound);
|
||||||
|
|
||||||
|
// Pop the first request from the list.
|
||||||
|
client_thread = m_thread_request_list.front();
|
||||||
|
m_thread_request_list.pop_front();
|
||||||
|
|
||||||
|
// Get the thread for the request.
|
||||||
|
R_UNLESS(client_thread != nullptr, ResultSessionClosed);
|
||||||
|
|
||||||
|
// Open the client thread.
|
||||||
|
client_thread->Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCOPE_EXIT({ client_thread->Close(); });
|
||||||
|
|
||||||
|
// Set the request as our current.
|
||||||
|
m_current_thread_request = client_thread;
|
||||||
|
|
||||||
|
// Receive the message.
|
||||||
|
Core::Memory::Memory& memory{kernel.System().Memory()};
|
||||||
|
KThread* server_thread{GetCurrentThreadPointer(kernel)};
|
||||||
|
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
||||||
|
|
||||||
|
auto* src_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
|
||||||
|
auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
||||||
|
std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KServerSession::CleanupRequests() {
|
||||||
|
KScopedLightLock lk(m_lock);
|
||||||
|
|
||||||
|
// Clean up any pending requests.
|
||||||
|
while (true) {
|
||||||
|
// Get the next request.
|
||||||
|
// KSessionRequest *request = nullptr;
|
||||||
|
KThread* client_thread = nullptr;
|
||||||
|
{
|
||||||
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
|
if (m_current_thread_request) {
|
||||||
|
// Choose the current request if we have one.
|
||||||
|
client_thread = m_current_thread_request;
|
||||||
|
m_current_thread_request = nullptr;
|
||||||
|
} else if (!m_thread_request_list.empty()) {
|
||||||
|
// Pop the request from the front of the list.
|
||||||
|
client_thread = m_thread_request_list.front();
|
||||||
|
m_thread_request_list.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no request, we're done.
|
||||||
|
if (client_thread == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close a reference to the request once it's cleaned up.
|
||||||
|
SCOPE_EXIT({ client_thread->Close(); });
|
||||||
|
|
||||||
|
// Extract relevant information from the request.
|
||||||
|
// const uintptr_t client_message = request->GetAddress();
|
||||||
|
// const size_t client_buffer_size = request->GetSize();
|
||||||
|
// KThread *client_thread = request->GetThread();
|
||||||
|
// KEvent *event = request->GetEvent();
|
||||||
|
|
||||||
|
// KProcess *server_process = request->GetServerProcess();
|
||||||
|
// KProcess *client_process = (client_thread != nullptr) ?
|
||||||
|
// client_thread->GetOwnerProcess() : nullptr;
|
||||||
|
// KProcessPageTable *client_page_table = (client_process != nullptr) ?
|
||||||
|
// &client_process->GetPageTable() : nullptr;
|
||||||
|
|
||||||
|
// Cleanup the mappings.
|
||||||
|
// Result result = CleanupMap(request, server_process, client_page_table);
|
||||||
|
|
||||||
|
// If there's a client thread, update it.
|
||||||
|
if (client_thread != nullptr) {
|
||||||
|
// End the client thread's wait.
|
||||||
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
|
if (!client_thread->IsTerminationRequested()) {
|
||||||
|
client_thread->EndWait(ResultSessionClosed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -10,6 +11,7 @@
|
||||||
#include <boost/intrusive/list.hpp>
|
#include <boost/intrusive/list.hpp>
|
||||||
|
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
|
#include "core/hle/kernel/k_light_lock.h"
|
||||||
#include "core/hle/kernel/k_synchronization_object.h"
|
#include "core/hle/kernel/k_synchronization_object.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
@ -59,25 +61,15 @@ public:
|
||||||
void OnClientClosed();
|
void OnClientClosed();
|
||||||
|
|
||||||
void ClientConnected(SessionRequestHandlerPtr handler) {
|
void ClientConnected(SessionRequestHandlerPtr handler) {
|
||||||
|
if (manager) {
|
||||||
manager->SetSessionHandler(std::move(handler));
|
manager->SetSessionHandler(std::move(handler));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ClientDisconnected() {
|
void ClientDisconnected() {
|
||||||
manager = nullptr;
|
manager = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a sync request from the emulated application.
|
|
||||||
*
|
|
||||||
* @param thread Thread that initiated the request.
|
|
||||||
* @param memory Memory context to handle the sync request under.
|
|
||||||
* @param core_timing Core timing context to schedule the request event under.
|
|
||||||
*
|
|
||||||
* @returns Result from the operation.
|
|
||||||
*/
|
|
||||||
Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
|
||||||
Core::Timing::CoreTiming& core_timing);
|
|
||||||
|
|
||||||
/// Adds a new domain request handler to the collection of request handlers within
|
/// Adds a new domain request handler to the collection of request handlers within
|
||||||
/// this ServerSession instance.
|
/// this ServerSession instance.
|
||||||
void AppendDomainHandler(SessionRequestHandlerPtr handler);
|
void AppendDomainHandler(SessionRequestHandlerPtr handler);
|
||||||
|
@ -88,7 +80,7 @@ public:
|
||||||
|
|
||||||
/// Returns true if the session has been converted to a domain, otherwise False
|
/// Returns true if the session has been converted to a domain, otherwise False
|
||||||
bool IsDomain() const {
|
bool IsDomain() const {
|
||||||
return manager->IsDomain();
|
return manager && manager->IsDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the session to a domain at the end of the current command
|
/// Converts the session to a domain at the end of the current command
|
||||||
|
@ -101,7 +93,15 @@ public:
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: flesh these out to match the real kernel
|
||||||
|
Result OnRequest();
|
||||||
|
Result SendReply();
|
||||||
|
Result ReceiveRequest();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Frees up waiting client sessions when this server session is about to die
|
||||||
|
void CleanupRequests();
|
||||||
|
|
||||||
/// Queues a sync request from the emulated application.
|
/// Queues a sync request from the emulated application.
|
||||||
Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
|
Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ private:
|
||||||
/// object handle.
|
/// object handle.
|
||||||
Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
|
Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
|
||||||
|
|
||||||
/// This session's HLE request handlers
|
/// This session's HLE request handlers; if nullptr, this is not an HLE server
|
||||||
std::shared_ptr<SessionRequestManager> manager;
|
std::shared_ptr<SessionRequestManager> manager;
|
||||||
|
|
||||||
/// When set to True, converts the session to a domain at the end of the command
|
/// When set to True, converts the session to a domain at the end of the command
|
||||||
|
@ -120,6 +120,13 @@ private:
|
||||||
|
|
||||||
/// KSession that owns this KServerSession
|
/// KSession that owns this KServerSession
|
||||||
KSession* parent{};
|
KSession* parent{};
|
||||||
|
|
||||||
|
/// List of threads which are pending a reply.
|
||||||
|
/// FIXME: KSessionRequest
|
||||||
|
std::list<KThread*> m_thread_request_list;
|
||||||
|
KThread* m_current_thread_request{};
|
||||||
|
|
||||||
|
KLightLock m_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "core/hle/kernel/k_resource_limit.h"
|
#include "core/hle/kernel/k_resource_limit.h"
|
||||||
#include "core/hle/kernel/k_scheduler.h"
|
#include "core/hle/kernel/k_scheduler.h"
|
||||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||||
|
#include "core/hle/kernel/k_session.h"
|
||||||
#include "core/hle/kernel/k_shared_memory.h"
|
#include "core/hle/kernel/k_shared_memory.h"
|
||||||
#include "core/hle/kernel/k_synchronization_object.h"
|
#include "core/hle/kernel/k_synchronization_object.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
|
@ -256,6 +257,93 @@ static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u3
|
||||||
return UnmapMemory(system, dst_addr, src_addr, size);
|
return UnmapMemory(system, dst_addr, src_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
|
||||||
|
auto& process = *system.CurrentProcess();
|
||||||
|
auto& handle_table = process.GetHandleTable();
|
||||||
|
|
||||||
|
// Declare the session we're going to allocate.
|
||||||
|
T* session;
|
||||||
|
|
||||||
|
// Reserve a new session from the process resource limit.
|
||||||
|
// FIXME: LimitableResource_SessionCountMax
|
||||||
|
KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions);
|
||||||
|
if (session_reservation.Succeeded()) {
|
||||||
|
session = T::Create(system.Kernel());
|
||||||
|
} else {
|
||||||
|
return ResultLimitReached;
|
||||||
|
|
||||||
|
// // We couldn't reserve a session. Check that we support dynamically expanding the
|
||||||
|
// // resource limit.
|
||||||
|
// R_UNLESS(process.GetResourceLimit() ==
|
||||||
|
// &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
|
||||||
|
// R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
|
||||||
|
|
||||||
|
// // Try to allocate a session from unused slab memory.
|
||||||
|
// session = T::CreateFromUnusedSlabMemory();
|
||||||
|
// R_UNLESS(session != nullptr, ResultLimitReached);
|
||||||
|
// ON_RESULT_FAILURE { session->Close(); };
|
||||||
|
|
||||||
|
// // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
|
||||||
|
// // prevent request exhaustion.
|
||||||
|
// // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
|
||||||
|
// // no reason to not do this statically.
|
||||||
|
// if constexpr (std::same_as<T, KSession>) {
|
||||||
|
// for (size_t i = 0; i < 2; i++) {
|
||||||
|
// KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
|
||||||
|
// R_UNLESS(request != nullptr, ResultLimitReached);
|
||||||
|
// request->Close();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// We successfully allocated a session, so add the object we allocated to the resource
|
||||||
|
// limit.
|
||||||
|
// system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we successfully created a session.
|
||||||
|
R_UNLESS(session != nullptr, ResultOutOfResource);
|
||||||
|
|
||||||
|
// Initialize the session.
|
||||||
|
session->Initialize(nullptr, fmt::format("{}", name));
|
||||||
|
|
||||||
|
// Commit the session reservation.
|
||||||
|
session_reservation.Commit();
|
||||||
|
|
||||||
|
// Ensure that we clean up the session (and its only references are handle table) on function
|
||||||
|
// end.
|
||||||
|
SCOPE_EXIT({
|
||||||
|
session->GetClientSession().Close();
|
||||||
|
session->GetServerSession().Close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register the session.
|
||||||
|
T::Register(system.Kernel(), session);
|
||||||
|
|
||||||
|
// Add the server session to the handle table.
|
||||||
|
R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
|
||||||
|
|
||||||
|
// Add the client session to the handle table.
|
||||||
|
const auto result = handle_table.Add(out_client, &session->GetClientSession());
|
||||||
|
|
||||||
|
if (!R_SUCCEEDED(result)) {
|
||||||
|
// Ensure that we maintaing a clean handle state on exit.
|
||||||
|
handle_table.Remove(*out_server);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client,
|
||||||
|
u32 is_light, u64 name) {
|
||||||
|
if (is_light) {
|
||||||
|
// return CreateSession<KLightSession>(system, out_server, out_client, name);
|
||||||
|
return ResultUnknown;
|
||||||
|
} else {
|
||||||
|
return CreateSession<KSession>(system, out_server, out_client, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||||
static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
|
static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
|
||||||
auto& memory = system.Memory();
|
auto& memory = system.Memory();
|
||||||
|
@ -295,7 +383,8 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n
|
||||||
|
|
||||||
// Create a session.
|
// Create a session.
|
||||||
KClientSession* session{};
|
KClientSession* session{};
|
||||||
R_TRY(port->CreateSession(std::addressof(session)));
|
R_TRY(port->CreateSession(std::addressof(session),
|
||||||
|
std::make_shared<SessionRequestManager>(kernel)));
|
||||||
port->Close();
|
port->Close();
|
||||||
|
|
||||||
// Register the session in the table, close the extra reference.
|
// Register the session in the table, close the extra reference.
|
||||||
|
@ -313,7 +402,7 @@ static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
|
||||||
return ConnectToNamedPort(system, out_handle, port_name_address);
|
return ConnectToNamedPort(system, out_handle, port_name_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a blocking IPC call to an OS service.
|
/// Makes a blocking IPC call to a service.
|
||||||
static Result SendSyncRequest(Core::System& system, Handle handle) {
|
static Result SendSyncRequest(Core::System& system, Handle handle) {
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
|
|
||||||
|
@ -327,22 +416,75 @@ static Result SendSyncRequest(Core::System& system, Handle handle) {
|
||||||
|
|
||||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
||||||
|
|
||||||
{
|
return session->SendSyncRequest();
|
||||||
KScopedSchedulerLock lock(kernel);
|
|
||||||
|
|
||||||
// This is a synchronous request, so we should wait for our request to complete.
|
|
||||||
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
|
|
||||||
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
|
||||||
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetCurrentThread(kernel).GetWaitResult();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result SendSyncRequest32(Core::System& system, Handle handle) {
|
static Result SendSyncRequest32(Core::System& system, Handle handle) {
|
||||||
return SendSyncRequest(system, handle);
|
return SendSyncRequest(system, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles,
|
||||||
|
s32 num_handles, Handle reply_target, s64 timeout_ns) {
|
||||||
|
auto& kernel = system.Kernel();
|
||||||
|
auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
|
||||||
|
|
||||||
|
// Convert handle list to object table.
|
||||||
|
std::vector<KSynchronizationObject*> objs(num_handles);
|
||||||
|
R_UNLESS(
|
||||||
|
handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
|
||||||
|
ResultInvalidHandle);
|
||||||
|
|
||||||
|
// Ensure handles are closed when we're done.
|
||||||
|
SCOPE_EXIT({
|
||||||
|
for (auto i = 0; i < num_handles; ++i) {
|
||||||
|
objs[i]->Close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reply to the target, if one is specified.
|
||||||
|
if (reply_target != InvalidHandle) {
|
||||||
|
KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
|
||||||
|
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||||
|
|
||||||
|
// If we fail to reply, we want to set the output index to -1.
|
||||||
|
// ON_RESULT_FAILURE { *out_index = -1; };
|
||||||
|
|
||||||
|
// Send the reply.
|
||||||
|
// R_TRY(session->SendReply());
|
||||||
|
|
||||||
|
Result rc = session->SendReply();
|
||||||
|
if (!R_SUCCEEDED(rc)) {
|
||||||
|
*out_index = -1;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a message.
|
||||||
|
while (true) {
|
||||||
|
// Wait for an object.
|
||||||
|
s32 index;
|
||||||
|
Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
|
||||||
|
static_cast<s32>(objs.size()), timeout_ns);
|
||||||
|
if (result == ResultTimedOut) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive the request.
|
||||||
|
if (R_SUCCEEDED(result)) {
|
||||||
|
KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
|
||||||
|
if (session != nullptr) {
|
||||||
|
result = session->ReceiveRequest();
|
||||||
|
if (result == ResultNotFound) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_index = index;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the ID for the specified thread.
|
/// Get the ID for the specified thread.
|
||||||
static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
|
static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
|
||||||
// Get the thread from its handle.
|
// Get the thread from its handle.
|
||||||
|
@ -2860,10 +3002,10 @@ static const FunctionDef SVC_Table_64[] = {
|
||||||
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
|
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
|
||||||
{0x3E, nullptr, "Unknown3e"},
|
{0x3E, nullptr, "Unknown3e"},
|
||||||
{0x3F, nullptr, "Unknown3f"},
|
{0x3F, nullptr, "Unknown3f"},
|
||||||
{0x40, nullptr, "CreateSession"},
|
{0x40, SvcWrap64<CreateSession>, "CreateSession"},
|
||||||
{0x41, nullptr, "AcceptSession"},
|
{0x41, nullptr, "AcceptSession"},
|
||||||
{0x42, nullptr, "ReplyAndReceiveLight"},
|
{0x42, nullptr, "ReplyAndReceiveLight"},
|
||||||
{0x43, nullptr, "ReplyAndReceive"},
|
{0x43, SvcWrap64<ReplyAndReceive>, "ReplyAndReceive"},
|
||||||
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
|
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
|
||||||
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
|
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
|
||||||
{0x46, nullptr, "MapIoRegion"},
|
{0x46, nullptr, "MapIoRegion"},
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/svc_types.h"
|
#include "core/hle/kernel/svc_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -346,6 +347,37 @@ void SvcWrap64(Core::System& system) {
|
||||||
FuncReturn(system, retval);
|
FuncReturn(system, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used by CreateSession
|
||||||
|
template <Result func(Core::System&, Handle*, Handle*, u32, u64)>
|
||||||
|
void SvcWrap64(Core::System& system) {
|
||||||
|
Handle param_1 = 0;
|
||||||
|
Handle param_2 = 0;
|
||||||
|
const u32 retval = func(system, ¶m_1, ¶m_2, static_cast<u32>(Param(system, 2)),
|
||||||
|
static_cast<u32>(Param(system, 3)))
|
||||||
|
.raw;
|
||||||
|
|
||||||
|
system.CurrentArmInterface().SetReg(1, param_1);
|
||||||
|
system.CurrentArmInterface().SetReg(2, param_2);
|
||||||
|
FuncReturn(system, retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by ReplyAndReceive
|
||||||
|
template <Result func(Core::System&, s32*, Handle*, s32, Handle, s64)>
|
||||||
|
void SvcWrap64(Core::System& system) {
|
||||||
|
s32 param_1 = 0;
|
||||||
|
s32 num_handles = static_cast<s32>(Param(system, 2));
|
||||||
|
|
||||||
|
std::vector<Handle> handles(num_handles);
|
||||||
|
system.Memory().ReadBlock(Param(system, 1), handles.data(), num_handles * sizeof(Handle));
|
||||||
|
|
||||||
|
const u32 retval = func(system, ¶m_1, handles.data(), num_handles,
|
||||||
|
static_cast<s32>(Param(system, 3)), static_cast<s64>(Param(system, 4)))
|
||||||
|
.raw;
|
||||||
|
|
||||||
|
system.CurrentArmInterface().SetReg(1, param_1);
|
||||||
|
FuncReturn(system, retval);
|
||||||
|
}
|
||||||
|
|
||||||
// Used by WaitForAddress
|
// Used by WaitForAddress
|
||||||
template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
|
template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
|
||||||
void SvcWrap64(Core::System& system) {
|
void SvcWrap64(Core::System& system) {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
|
#include "core/hle/service/glue/glue_manager.h"
|
||||||
#include "core/hle/service/ns/errors.h"
|
#include "core/hle/service/ns/errors.h"
|
||||||
#include "core/hle/service/ns/iplatform_service_manager.h"
|
#include "core/hle/service/ns/iplatform_service_manager.h"
|
||||||
#include "core/hle/service/ns/language.h"
|
#include "core/hle/service/ns/language.h"
|
||||||
|
@ -581,7 +582,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||||
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
|
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "GetApplicationControlData"},
|
{0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"},
|
||||||
{1, nullptr, "GetApplicationDesiredLanguage"},
|
{1, nullptr, "GetApplicationDesiredLanguage"},
|
||||||
{2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
|
{2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
|
||||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||||
|
@ -594,6 +595,33 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||||
|
|
||||||
IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
|
IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
|
||||||
|
|
||||||
|
void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
||||||
|
Kernel::HLERequestContext& ctx) {
|
||||||
|
enum class ApplicationControlSource : u8 {
|
||||||
|
CacheOnly,
|
||||||
|
Storage,
|
||||||
|
StorageOnly,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RequestParameters {
|
||||||
|
ApplicationControlSource source;
|
||||||
|
u64 application_id;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
|
||||||
|
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto parameters{rp.PopRaw<RequestParameters>()};
|
||||||
|
const auto nacp_data{system.GetARPManager().GetControlProperty(parameters.application_id)};
|
||||||
|
const auto result = nacp_data ? ResultSuccess : ResultUnknown;
|
||||||
|
|
||||||
|
if (nacp_data) {
|
||||||
|
ctx.WriteBuffer(nacp_data->data(), nacp_data->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
|
NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
|
|
|
@ -78,6 +78,9 @@ class IReadOnlyApplicationControlDataInterface final
|
||||||
public:
|
public:
|
||||||
explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
|
explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
|
||||||
~IReadOnlyApplicationControlDataInterface() override;
|
~IReadOnlyApplicationControlDataInterface() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void GetApplicationControlData(Kernel::HLERequestContext& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
class NS final : public ServiceFramework<NS> {
|
class NS final : public ServiceFramework<NS> {
|
||||||
|
|
|
@ -15,7 +15,7 @@ TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
|
||||||
{0, nullptr, "GetTemperatureRange"},
|
{0, nullptr, "GetTemperatureRange"},
|
||||||
{1, &TS::GetTemperature, "GetTemperature"},
|
{1, &TS::GetTemperature, "GetTemperature"},
|
||||||
{2, nullptr, "SetMeasurementMode"},
|
{2, nullptr, "SetMeasurementMode"},
|
||||||
{3, nullptr, "GetTemperatureMilliC"},
|
{3, &TS::GetTemperatureMilliC, "GetTemperatureMilliC"},
|
||||||
{4, nullptr, "OpenSession"},
|
{4, nullptr, "OpenSession"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
@ -29,8 +29,6 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto location{rp.PopEnum<Location>()};
|
const auto location{rp.PopEnum<Location>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
|
|
||||||
|
|
||||||
const s32 temperature = location == Location::Internal ? 35 : 20;
|
const s32 temperature = location == Location::Internal ? 35 : 20;
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
@ -38,4 +36,15 @@ void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
|
||||||
rb.Push(temperature);
|
rb.Push(temperature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TS::GetTemperatureMilliC(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto location{rp.PopEnum<Location>()};
|
||||||
|
|
||||||
|
const s32 temperature = location == Location::Internal ? 35000 : 20000;
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(temperature);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::PTM
|
} // namespace Service::PTM
|
||||||
|
|
|
@ -20,6 +20,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
void GetTemperature(Kernel::HLERequestContext& ctx);
|
void GetTemperature(Kernel::HLERequestContext& ctx);
|
||||||
|
void GetTemperatureMilliC(Kernel::HLERequestContext& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::PTM
|
} // namespace Service::PTM
|
||||||
|
|
|
@ -101,6 +101,81 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) {
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: implement support for the real system_settings.ini
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static std::vector<u8> ToBytes(const T& value) {
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>);
|
||||||
|
|
||||||
|
const auto* begin = reinterpret_cast<const u8*>(&value);
|
||||||
|
const auto* end = begin + sizeof(T);
|
||||||
|
|
||||||
|
return std::vector<u8>(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
using Settings =
|
||||||
|
std::map<std::string, std::map<std::string, std::vector<u8>, std::less<>>, std::less<>>;
|
||||||
|
|
||||||
|
static Settings GetSettings() {
|
||||||
|
Settings ret;
|
||||||
|
|
||||||
|
ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0});
|
||||||
|
ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000});
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SET_SYS::GetSettingsItemValueSize(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_SET, "called");
|
||||||
|
|
||||||
|
// The category of the setting. This corresponds to the top-level keys of
|
||||||
|
// system_settings.ini.
|
||||||
|
const auto setting_category_buf{ctx.ReadBuffer(0)};
|
||||||
|
const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
|
||||||
|
|
||||||
|
// The name of the setting. This corresponds to the second-level keys of
|
||||||
|
// system_settings.ini.
|
||||||
|
const auto setting_name_buf{ctx.ReadBuffer(1)};
|
||||||
|
const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
|
||||||
|
|
||||||
|
auto settings{GetSettings()};
|
||||||
|
u64 response_size{0};
|
||||||
|
|
||||||
|
if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
|
||||||
|
response_size = settings[setting_category][setting_name].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess);
|
||||||
|
rb.Push(response_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_SET, "called");
|
||||||
|
|
||||||
|
// The category of the setting. This corresponds to the top-level keys of
|
||||||
|
// system_settings.ini.
|
||||||
|
const auto setting_category_buf{ctx.ReadBuffer(0)};
|
||||||
|
const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()};
|
||||||
|
|
||||||
|
// The name of the setting. This corresponds to the second-level keys of
|
||||||
|
// system_settings.ini.
|
||||||
|
const auto setting_name_buf{ctx.ReadBuffer(1)};
|
||||||
|
const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()};
|
||||||
|
|
||||||
|
auto settings{GetSettings()};
|
||||||
|
Result response{ResultUnknown};
|
||||||
|
|
||||||
|
if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) {
|
||||||
|
auto setting_value = settings[setting_category][setting_name];
|
||||||
|
ctx.WriteBuffer(setting_value.data(), setting_value.size());
|
||||||
|
response = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(response);
|
||||||
|
}
|
||||||
|
|
||||||
SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
|
SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
|
@ -138,8 +213,8 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
|
||||||
{32, nullptr, "SetAccountNotificationSettings"},
|
{32, nullptr, "SetAccountNotificationSettings"},
|
||||||
{35, nullptr, "GetVibrationMasterVolume"},
|
{35, nullptr, "GetVibrationMasterVolume"},
|
||||||
{36, nullptr, "SetVibrationMasterVolume"},
|
{36, nullptr, "SetVibrationMasterVolume"},
|
||||||
{37, nullptr, "GetSettingsItemValueSize"},
|
{37, &SET_SYS::GetSettingsItemValueSize, "GetSettingsItemValueSize"},
|
||||||
{38, nullptr, "GetSettingsItemValue"},
|
{38, &SET_SYS::GetSettingsItemValue, "GetSettingsItemValue"},
|
||||||
{39, nullptr, "GetTvSettings"},
|
{39, nullptr, "GetTvSettings"},
|
||||||
{40, nullptr, "SetTvSettings"},
|
{40, nullptr, "SetTvSettings"},
|
||||||
{41, nullptr, "GetEdid"},
|
{41, nullptr, "GetEdid"},
|
||||||
|
|
|
@ -23,6 +23,8 @@ private:
|
||||||
BasicBlack = 1,
|
BasicBlack = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void GetSettingsItemValueSize(Kernel::HLERequestContext& ctx);
|
||||||
|
void GetSettingsItemValue(Kernel::HLERequestContext& ctx);
|
||||||
void GetFirmwareVersion(Kernel::HLERequestContext& ctx);
|
void GetFirmwareVersion(Kernel::HLERequestContext& ctx);
|
||||||
void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
|
void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
|
||||||
void GetColorSetId(Kernel::HLERequestContext& ctx);
|
void GetColorSetId(Kernel::HLERequestContext& ctx);
|
||||||
|
|
|
@ -156,7 +156,8 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
|
||||||
|
|
||||||
// Create a new session.
|
// Create a new session.
|
||||||
Kernel::KClientSession* session{};
|
Kernel::KClientSession* session{};
|
||||||
if (const auto result = port->GetClientPort().CreateSession(std::addressof(session));
|
if (const auto result = port->GetClientPort().CreateSession(
|
||||||
|
std::addressof(session), std::make_shared<Kernel::SessionRequestManager>(kernel));
|
||||||
result.IsError()) {
|
result.IsError()) {
|
||||||
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
|
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
|
||||||
return result;
|
return result;
|
||||||
|
|
Loading…
Reference in a new issue