mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #2774 from yuriks/hle-handles
Add basic support for IPC translation for HLE services
This commit is contained in:
		
						commit
						4325a7c82d
					
				
							
								
								
									
										2
									
								
								externals/boost
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										2
									
								
								externals/boost
									
									
									
									
										vendored
									
									
								
							@ -1 +1 @@
 | 
			
		||||
Subproject commit 351972396392c97a659b9a02f34ce9269293d21f
 | 
			
		||||
Subproject commit 3abc84abaf63a068cb59a9f9b5675c1947bc6fd9
 | 
			
		||||
@ -44,6 +44,9 @@ inline u32* GetStaticBuffers(const int offset = 0) {
 | 
			
		||||
 | 
			
		||||
namespace IPC {
 | 
			
		||||
 | 
			
		||||
/// Size of the command buffer area, in 32-bit words.
 | 
			
		||||
constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
 | 
			
		||||
 | 
			
		||||
// These errors are commonly returned by invalid IPC translations, so alias them here for
 | 
			
		||||
// convenience.
 | 
			
		||||
// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
 | 
			
		||||
 | 
			
		||||
@ -4,19 +4,28 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include "core/hle/ipc.h"
 | 
			
		||||
#include "core/hle/kernel/handle_table.h"
 | 
			
		||||
#include "core/hle/kernel/hle_ipc.h"
 | 
			
		||||
#include "core/hle/kernel/kernel.h"
 | 
			
		||||
 | 
			
		||||
namespace IPC {
 | 
			
		||||
 | 
			
		||||
class RequestHelperBase {
 | 
			
		||||
protected:
 | 
			
		||||
    Kernel::HLERequestContext* context = nullptr;
 | 
			
		||||
    u32* cmdbuf;
 | 
			
		||||
    ptrdiff_t index = 1;
 | 
			
		||||
    Header header;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    RequestHelperBase(Kernel::HLERequestContext& context, Header desired_header)
 | 
			
		||||
        : context(&context), cmdbuf(context.CommandBuffer()), header(desired_header) {}
 | 
			
		||||
 | 
			
		||||
    RequestHelperBase(u32* command_buffer, Header command_header)
 | 
			
		||||
        : cmdbuf(command_buffer), header(command_header) {}
 | 
			
		||||
 | 
			
		||||
@ -51,12 +60,27 @@ public:
 | 
			
		||||
 | 
			
		||||
class RequestBuilder : public RequestHelperBase {
 | 
			
		||||
public:
 | 
			
		||||
    RequestBuilder(Kernel::HLERequestContext& context, Header command_header)
 | 
			
		||||
        : RequestHelperBase(context, command_header) {
 | 
			
		||||
        // From this point we will start overwriting the existing command buffer, so it's safe to
 | 
			
		||||
        // release all previous incoming Object pointers since they won't be usable anymore.
 | 
			
		||||
        context.ClearIncomingObjects();
 | 
			
		||||
        cmdbuf[0] = header.raw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RequestBuilder(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size,
 | 
			
		||||
                   unsigned translate_params_size)
 | 
			
		||||
        : RequestBuilder(
 | 
			
		||||
              context, Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {}
 | 
			
		||||
 | 
			
		||||
    RequestBuilder(u32* command_buffer, Header command_header)
 | 
			
		||||
        : RequestHelperBase(command_buffer, command_header) {
 | 
			
		||||
        cmdbuf[0] = header.raw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    explicit RequestBuilder(u32* command_buffer, u32 command_header)
 | 
			
		||||
        : RequestBuilder(command_buffer, Header{command_header}) {}
 | 
			
		||||
 | 
			
		||||
    RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size,
 | 
			
		||||
                   unsigned translate_params_size)
 | 
			
		||||
        : RequestBuilder(command_buffer,
 | 
			
		||||
@ -88,6 +112,9 @@ public:
 | 
			
		||||
    template <typename... H>
 | 
			
		||||
    void PushMoveHandles(H... handles);
 | 
			
		||||
 | 
			
		||||
    template <typename... O>
 | 
			
		||||
    void PushObjects(Kernel::SharedPtr<O>... pointers);
 | 
			
		||||
 | 
			
		||||
    void PushCurrentPIDHandle();
 | 
			
		||||
 | 
			
		||||
    void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id);
 | 
			
		||||
@ -153,6 +180,11 @@ inline void RequestBuilder::PushMoveHandles(H... handles) {
 | 
			
		||||
    Push(static_cast<Kernel::Handle>(handles)...);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename... O>
 | 
			
		||||
inline void RequestBuilder::PushObjects(Kernel::SharedPtr<O>... pointers) {
 | 
			
		||||
    PushMoveHandles(context->AddOutgoingHandle(std::move(pointers))...);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void RequestBuilder::PushCurrentPIDHandle() {
 | 
			
		||||
    Push(CallingPidDesc());
 | 
			
		||||
    Push(u32(0));
 | 
			
		||||
@ -171,10 +203,21 @@ inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size,
 | 
			
		||||
 | 
			
		||||
class RequestParser : public RequestHelperBase {
 | 
			
		||||
public:
 | 
			
		||||
    RequestParser(Kernel::HLERequestContext& context, Header desired_header)
 | 
			
		||||
        : RequestHelperBase(context, desired_header) {}
 | 
			
		||||
 | 
			
		||||
    RequestParser(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size,
 | 
			
		||||
                  unsigned translate_params_size)
 | 
			
		||||
        : RequestParser(context,
 | 
			
		||||
                        Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RequestParser(u32* command_buffer, Header command_header)
 | 
			
		||||
        : RequestHelperBase(command_buffer, command_header) {}
 | 
			
		||||
 | 
			
		||||
    explicit RequestParser(u32* command_buffer, u32 command_header)
 | 
			
		||||
        : RequestParser(command_buffer, Header{command_header}) {}
 | 
			
		||||
 | 
			
		||||
    RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size,
 | 
			
		||||
                  unsigned translate_params_size)
 | 
			
		||||
        : RequestParser(command_buffer,
 | 
			
		||||
@ -186,6 +229,9 @@ public:
 | 
			
		||||
            ValidateHeader();
 | 
			
		||||
        Header builderHeader{
 | 
			
		||||
            MakeHeader(header.command_id, normal_params_size, translate_params_size)};
 | 
			
		||||
        if (context != nullptr)
 | 
			
		||||
            return {*context, builderHeader};
 | 
			
		||||
        else
 | 
			
		||||
            return {cmdbuf, builderHeader};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -198,10 +244,52 @@ public:
 | 
			
		||||
    template <typename First, typename... Other>
 | 
			
		||||
    void Pop(First& first_value, Other&... other_values);
 | 
			
		||||
 | 
			
		||||
    /// Equivalent to calling `PopHandles<1>()[0]`.
 | 
			
		||||
    Kernel::Handle PopHandle();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pops a descriptor containing `N` handles. The handles are returned as an array. The
 | 
			
		||||
     * descriptor must contain exactly `N` handles, it is not permitted to, for example, call
 | 
			
		||||
     * PopHandles<1>() twice to read a multi-handle descriptor with 2 handles, or to make a single
 | 
			
		||||
     * PopHandles<2>() call to read 2 single-handle descriptors.
 | 
			
		||||
     */
 | 
			
		||||
    template <unsigned int N>
 | 
			
		||||
    std::array<Kernel::Handle, N> PopHandles();
 | 
			
		||||
 | 
			
		||||
    /// Convenience wrapper around PopHandles() which assigns the handles to the passed references.
 | 
			
		||||
    template <typename... H>
 | 
			
		||||
    void PopHandles(H&... handles);
 | 
			
		||||
    void PopHandles(H&... handles) {
 | 
			
		||||
        std::tie(handles...) = PopHandles<sizeof...(H)>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Equivalent to calling `PopGenericObjects<1>()[0]`.
 | 
			
		||||
    Kernel::SharedPtr<Kernel::Object> PopGenericObject();
 | 
			
		||||
 | 
			
		||||
    /// Equivalent to calling `std::get<0>(PopObjects<T>())`.
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    Kernel::SharedPtr<T> PopObject();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pop a descriptor containing `N` handles and resolves them to Kernel::Object pointers. If a
 | 
			
		||||
     * handle is invalid, null is returned for that object instead. The same caveats from
 | 
			
		||||
     * PopHandles() apply regarding `N` matching the number of handles in the descriptor.
 | 
			
		||||
     */
 | 
			
		||||
    template <unsigned int N>
 | 
			
		||||
    std::array<Kernel::SharedPtr<Kernel::Object>, N> PopGenericObjects();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolves handles to Kernel::Objects as in PopGenericsObjects(), but then also casts them to
 | 
			
		||||
     * the passed `T` types, while verifying that the cast is valid. If the type of an object does
 | 
			
		||||
     * not match, null is returned instead.
 | 
			
		||||
     */
 | 
			
		||||
    template <typename... T>
 | 
			
		||||
    std::tuple<Kernel::SharedPtr<T>...> PopObjects();
 | 
			
		||||
 | 
			
		||||
    /// Convenience wrapper around PopObjects() which assigns the handles to the passed references.
 | 
			
		||||
    template <typename... T>
 | 
			
		||||
    void PopObjects(Kernel::SharedPtr<T>&... pointers) {
 | 
			
		||||
        std::tie(pointers...) = PopObjects<T...>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Pops the static buffer vaddr
 | 
			
		||||
@ -313,15 +401,54 @@ inline Kernel::Handle RequestParser::PopHandle() {
 | 
			
		||||
    return Pop<Kernel::Handle>();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename... H>
 | 
			
		||||
void RequestParser::PopHandles(H&... handles) {
 | 
			
		||||
    const u32 handle_descriptor = Pop<u32>();
 | 
			
		||||
    const int handles_number = sizeof...(H);
 | 
			
		||||
    DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
 | 
			
		||||
template <unsigned int N>
 | 
			
		||||
std::array<Kernel::Handle, N> RequestParser::PopHandles() {
 | 
			
		||||
    u32 handle_descriptor = Pop<u32>();
 | 
			
		||||
    ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
 | 
			
		||||
               "Tried to pop handle(s) but the descriptor is not a handle descriptor");
 | 
			
		||||
    DEBUG_ASSERT_MSG(handles_number == HandleNumberFromDesc(handle_descriptor),
 | 
			
		||||
    ASSERT_MSG(N == HandleNumberFromDesc(handle_descriptor),
 | 
			
		||||
               "Number of handles doesn't match the descriptor");
 | 
			
		||||
    Pop(static_cast<Kernel::Handle&>(handles)...);
 | 
			
		||||
 | 
			
		||||
    std::array<Kernel::Handle, N> handles{};
 | 
			
		||||
    for (Kernel::Handle& handle : handles) {
 | 
			
		||||
        handle = Pop<Kernel::Handle>();
 | 
			
		||||
    }
 | 
			
		||||
    return handles;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline Kernel::SharedPtr<Kernel::Object> RequestParser::PopGenericObject() {
 | 
			
		||||
    Kernel::Handle handle = PopHandle();
 | 
			
		||||
    return context->GetIncomingHandle(handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
Kernel::SharedPtr<T> RequestParser::PopObject() {
 | 
			
		||||
    return Kernel::DynamicObjectCast<T>(PopGenericObject());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <unsigned int N>
 | 
			
		||||
inline std::array<Kernel::SharedPtr<Kernel::Object>, N> RequestParser::PopGenericObjects() {
 | 
			
		||||
    std::array<Kernel::Handle, N> handles = PopHandles<N>();
 | 
			
		||||
    std::array<Kernel::SharedPtr<Kernel::Object>, N> pointers;
 | 
			
		||||
    for (int i = 0; i < N; ++i) {
 | 
			
		||||
        pointers[i] = context->GetIncomingHandle(handles[i]);
 | 
			
		||||
    }
 | 
			
		||||
    return pointers;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace detail {
 | 
			
		||||
template <typename... T, size_t... I>
 | 
			
		||||
std::tuple<Kernel::SharedPtr<T>...> PopObjectsHelper(
 | 
			
		||||
    std::array<Kernel::SharedPtr<Kernel::Object>, sizeof...(T)>&& pointers,
 | 
			
		||||
    std::index_sequence<I...>) {
 | 
			
		||||
    return std::make_tuple(Kernel::DynamicObjectCast<T>(std::move(pointers[I]))...);
 | 
			
		||||
}
 | 
			
		||||
} // namespace detail
 | 
			
		||||
 | 
			
		||||
template <typename... T>
 | 
			
		||||
inline std::tuple<Kernel::SharedPtr<T>...> RequestParser::PopObjects() {
 | 
			
		||||
    return detail::PopObjectsHelper<T...>(PopGenericObjects<sizeof...(T)>(),
 | 
			
		||||
                                          std::index_sequence_for<T...>{});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) {
 | 
			
		||||
 | 
			
		||||
@ -5,8 +5,10 @@
 | 
			
		||||
#include <boost/range/algorithm_ext/erase.hpp>
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "core/hle/kernel/handle_table.h"
 | 
			
		||||
#include "core/hle/kernel/hle_ipc.h"
 | 
			
		||||
#include "core/hle/kernel/kernel.h"
 | 
			
		||||
#include "core/hle/kernel/process.h"
 | 
			
		||||
#include "core/hle/kernel/server_session.h"
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
@ -23,4 +25,101 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
 | 
			
		||||
 | 
			
		||||
HLERequestContext::~HLERequestContext() = default;
 | 
			
		||||
 | 
			
		||||
SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const {
 | 
			
		||||
    ASSERT(id_from_cmdbuf < request_handles.size());
 | 
			
		||||
    return request_handles[id_from_cmdbuf];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) {
 | 
			
		||||
    request_handles.push_back(std::move(object));
 | 
			
		||||
    return request_handles.size() - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HLERequestContext::ClearIncomingObjects() {
 | 
			
		||||
    request_handles.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf,
 | 
			
		||||
                                                                Process& src_process,
 | 
			
		||||
                                                                HandleTable& src_table) {
 | 
			
		||||
    IPC::Header header{src_cmdbuf[0]};
 | 
			
		||||
 | 
			
		||||
    size_t untranslated_size = 1u + header.normal_params_size;
 | 
			
		||||
    size_t command_size = untranslated_size + header.translate_params_size;
 | 
			
		||||
    ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error
 | 
			
		||||
 | 
			
		||||
    std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin());
 | 
			
		||||
 | 
			
		||||
    size_t i = untranslated_size;
 | 
			
		||||
    while (i < command_size) {
 | 
			
		||||
        u32 descriptor = cmd_buf[i] = src_cmdbuf[i];
 | 
			
		||||
        i += 1;
 | 
			
		||||
 | 
			
		||||
        switch (IPC::GetDescriptorType(descriptor)) {
 | 
			
		||||
        case IPC::DescriptorType::CopyHandle:
 | 
			
		||||
        case IPC::DescriptorType::MoveHandle: {
 | 
			
		||||
            u32 num_handles = IPC::HandleNumberFromDesc(descriptor);
 | 
			
		||||
            ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error
 | 
			
		||||
            for (u32 j = 0; j < num_handles; ++j) {
 | 
			
		||||
                Handle handle = src_cmdbuf[i];
 | 
			
		||||
                SharedPtr<Object> object = src_table.GetGeneric(handle);
 | 
			
		||||
                ASSERT(object != nullptr); // TODO(yuriks): Return error
 | 
			
		||||
                if (descriptor == IPC::DescriptorType::MoveHandle) {
 | 
			
		||||
                    src_table.Close(handle);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                cmd_buf[i++] = AddOutgoingHandle(std::move(object));
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case IPC::DescriptorType::CallingPid: {
 | 
			
		||||
            cmd_buf[i++] = src_process.process_id;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        default:
 | 
			
		||||
            UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
 | 
			
		||||
                                                           HandleTable& dst_table) const {
 | 
			
		||||
    IPC::Header header{cmd_buf[0]};
 | 
			
		||||
 | 
			
		||||
    size_t untranslated_size = 1u + header.normal_params_size;
 | 
			
		||||
    size_t command_size = untranslated_size + header.translate_params_size;
 | 
			
		||||
    ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH);
 | 
			
		||||
 | 
			
		||||
    std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf);
 | 
			
		||||
 | 
			
		||||
    size_t i = untranslated_size;
 | 
			
		||||
    while (i < command_size) {
 | 
			
		||||
        u32 descriptor = dst_cmdbuf[i] = cmd_buf[i];
 | 
			
		||||
        i += 1;
 | 
			
		||||
 | 
			
		||||
        switch (IPC::GetDescriptorType(descriptor)) {
 | 
			
		||||
        case IPC::DescriptorType::CopyHandle:
 | 
			
		||||
        case IPC::DescriptorType::MoveHandle: {
 | 
			
		||||
            // HLE services don't use handles, so we treat both CopyHandle and MoveHandle equally
 | 
			
		||||
            u32 num_handles = IPC::HandleNumberFromDesc(descriptor);
 | 
			
		||||
            ASSERT(i + num_handles <= command_size);
 | 
			
		||||
            for (u32 j = 0; j < num_handles; ++j) {
 | 
			
		||||
                SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]);
 | 
			
		||||
 | 
			
		||||
                // TODO(yuriks): Figure out the proper error handling for if this fails
 | 
			
		||||
                Handle handle = dst_table.Create(object).Unwrap();
 | 
			
		||||
                dst_cmdbuf[i++] = handle;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        default:
 | 
			
		||||
            UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,13 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <boost/container/small_vector.hpp>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "core/hle/ipc.h"
 | 
			
		||||
#include "core/hle/kernel/kernel.h"
 | 
			
		||||
#include "core/hle/kernel/server_session.h"
 | 
			
		||||
 | 
			
		||||
@ -15,6 +20,9 @@ class ServiceFrameworkBase;
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
 | 
			
		||||
class HandleTable;
 | 
			
		||||
class Process;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Interface implemented by HLE Session handlers.
 | 
			
		||||
 * This can be provided to a ServerSession in order to hook into several relevant events
 | 
			
		||||
@ -59,14 +67,28 @@ protected:
 | 
			
		||||
 * Class containing information about an in-flight IPC request being handled by an HLE service
 | 
			
		||||
 * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and
 | 
			
		||||
 * when possible use the APIs in this class to service the request.
 | 
			
		||||
 *
 | 
			
		||||
 * HLE handle protocol
 | 
			
		||||
 * ===================
 | 
			
		||||
 *
 | 
			
		||||
 * To avoid needing HLE services to keep a separate handle table, or having to directly modify the
 | 
			
		||||
 * requester's table, a tweaked protocol is used to receive and send handles in requests. The kernel
 | 
			
		||||
 * will decode the incoming handles into object pointers and insert a id in the buffer where the
 | 
			
		||||
 * handle would normally be. The service then calls GetIncomingHandle() with that id to get the
 | 
			
		||||
 * pointer to the object. Similarly, instead of inserting a handle into the command buffer, the
 | 
			
		||||
 * service calls AddOutgoingHandle() and stores the returned id where the handle would normally go.
 | 
			
		||||
 *
 | 
			
		||||
 * The end result is similar to just giving services their own real handle tables, but since these
 | 
			
		||||
 * ids are local to a specific context, it avoids requiring services to manage handles for objects
 | 
			
		||||
 * across multiple calls and ensuring that unneeded handles are cleaned up.
 | 
			
		||||
 */
 | 
			
		||||
class HLERequestContext {
 | 
			
		||||
public:
 | 
			
		||||
    ~HLERequestContext();
 | 
			
		||||
 | 
			
		||||
    /// Returns a pointer to the IPC command buffer for this request.
 | 
			
		||||
    u32* CommandBuffer() const {
 | 
			
		||||
        return cmd_buf;
 | 
			
		||||
    u32* CommandBuffer() {
 | 
			
		||||
        return cmd_buf.data();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -77,11 +99,37 @@ public:
 | 
			
		||||
        return session;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolves a object id from the request command buffer into a pointer to an object. See the
 | 
			
		||||
     * "HLE handle protocol" section in the class documentation for more details.
 | 
			
		||||
     */
 | 
			
		||||
    SharedPtr<Object> GetIncomingHandle(u32 id_from_cmdbuf) const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds an outgoing object to the response, returning the id which should be used to reference
 | 
			
		||||
     * it. See the "HLE handle protocol" section in the class documentation for more details.
 | 
			
		||||
     */
 | 
			
		||||
    u32 AddOutgoingHandle(SharedPtr<Object> object);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Discards all Objects from the context, invalidating all ids. This may be called after reading
 | 
			
		||||
     * out all incoming objects, so that the buffer memory can be re-used for outgoing handles, but
 | 
			
		||||
     * this is not required.
 | 
			
		||||
     */
 | 
			
		||||
    void ClearIncomingObjects();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend class Service::ServiceFrameworkBase;
 | 
			
		||||
 | 
			
		||||
    u32* cmd_buf = nullptr;
 | 
			
		||||
    ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process,
 | 
			
		||||
                                                 HandleTable& src_table);
 | 
			
		||||
    ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
 | 
			
		||||
                                            HandleTable& dst_table) const;
 | 
			
		||||
 | 
			
		||||
    std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
 | 
			
		||||
    SharedPtr<ServerSession> session;
 | 
			
		||||
    // TODO(yuriks): Check common usage of this and optimize size accordingly
 | 
			
		||||
    boost::container::small_vector<SharedPtr<Object>, 8> request_handles;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,14 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
#include "core/hle/ipc.h"
 | 
			
		||||
#include "core/hle/kernel/client_port.h"
 | 
			
		||||
#include "core/hle/kernel/process.h"
 | 
			
		||||
#include "core/hle/kernel/server_port.h"
 | 
			
		||||
#include "core/hle/kernel/server_session.h"
 | 
			
		||||
#include "core/hle/service/ac/ac.h"
 | 
			
		||||
@ -160,12 +164,6 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct
 | 
			
		||||
void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) {
 | 
			
		||||
    u32* cmd_buf = Kernel::GetCommandBuffer();
 | 
			
		||||
 | 
			
		||||
    // TODO(yuriks): The kernel should be the one handling this as part of translation after
 | 
			
		||||
    // everything else is migrated
 | 
			
		||||
    Kernel::HLERequestContext context;
 | 
			
		||||
    context.cmd_buf = cmd_buf;
 | 
			
		||||
    context.session = std::move(server_session);
 | 
			
		||||
 | 
			
		||||
    u32 header_code = cmd_buf[0];
 | 
			
		||||
    auto itr = handlers.find(header_code);
 | 
			
		||||
    const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
 | 
			
		||||
@ -173,9 +171,18 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses
 | 
			
		||||
        return ReportUnimplementedFunction(cmd_buf, info);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(yuriks): The kernel should be the one handling this as part of translation after
 | 
			
		||||
    // everything else is migrated
 | 
			
		||||
    Kernel::HLERequestContext context;
 | 
			
		||||
    context.session = std::move(server_session);
 | 
			
		||||
    context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
 | 
			
		||||
                                              Kernel::g_handle_table);
 | 
			
		||||
 | 
			
		||||
    LOG_TRACE(Service, "%s",
 | 
			
		||||
              MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str());
 | 
			
		||||
    handler_invoker(this, info->handler_callback, context);
 | 
			
		||||
    context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process,
 | 
			
		||||
                                         Kernel::g_handle_table);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
@ -7,9 +7,11 @@
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/hle/ipc.h"
 | 
			
		||||
#include "core/hle/ipc_helpers.h"
 | 
			
		||||
#include "core/hle/kernel/client_port.h"
 | 
			
		||||
#include "core/hle/kernel/client_session.h"
 | 
			
		||||
#include "core/hle/kernel/handle_table.h"
 | 
			
		||||
#include "core/hle/kernel/errors.h"
 | 
			
		||||
#include "core/hle/kernel/hle_ipc.h"
 | 
			
		||||
#include "core/hle/kernel/semaphore.h"
 | 
			
		||||
#include "core/hle/kernel/server_session.h"
 | 
			
		||||
#include "core/hle/service/sm/sm.h"
 | 
			
		||||
@ -30,15 +32,18 @@ constexpr int MAX_PENDING_NOTIFICATIONS = 16;
 | 
			
		||||
 *      1: ResultCode
 | 
			
		||||
 */
 | 
			
		||||
void SRV::RegisterClient(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    u32* cmd_buff = ctx.CommandBuffer();
 | 
			
		||||
    IPC::RequestParser rp(ctx, 0x1, 0, 2);
 | 
			
		||||
 | 
			
		||||
    if (cmd_buff[1] != IPC::CallingPidDesc()) {
 | 
			
		||||
        cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); // 0x40
 | 
			
		||||
        cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw;
 | 
			
		||||
    u32 pid_descriptor = rp.Pop<u32>();
 | 
			
		||||
    if (pid_descriptor != IPC::CallingPidDesc()) {
 | 
			
		||||
        IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
        rb.Push(IPC::ERR_INVALID_BUFFER_DESCRIPTOR);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); // 0x10040
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw;           // No error
 | 
			
		||||
    u32 caller_pid = rp.Pop<u32>();
 | 
			
		||||
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    LOG_WARNING(Service_SRV, "(STUBBED) called");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -53,15 +58,14 @@ void SRV::RegisterClient(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 *      3: Handle to semaphore signaled on process notification
 | 
			
		||||
 */
 | 
			
		||||
void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    u32* cmd_buff = ctx.CommandBuffer();
 | 
			
		||||
    IPC::RequestParser rp(ctx, 0x2, 0, 0);
 | 
			
		||||
 | 
			
		||||
    notification_semaphore =
 | 
			
		||||
        Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap();
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw;             // No error
 | 
			
		||||
    cmd_buff[2] = IPC::CopyHandleDesc(1);
 | 
			
		||||
    cmd_buff[3] = Kernel::g_handle_table.Create(notification_semaphore).MoveFrom();
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    rb.PushObjects(notification_semaphore);
 | 
			
		||||
    LOG_WARNING(Service_SRV, "(STUBBED) called");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -77,43 +81,49 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 *      3: Service handle
 | 
			
		||||
 */
 | 
			
		||||
void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    ResultCode res = RESULT_SUCCESS;
 | 
			
		||||
    u32* cmd_buff = ctx.CommandBuffer();
 | 
			
		||||
    IPC::RequestParser rp(ctx, 0x5, 4, 0);
 | 
			
		||||
    auto name_buf = rp.PopRaw<std::array<char, 8>>();
 | 
			
		||||
    size_t name_len = rp.Pop<u32>();
 | 
			
		||||
    u32 flags = rp.Pop<u32>();
 | 
			
		||||
 | 
			
		||||
    bool return_port_on_failure = (flags & 1) == 0;
 | 
			
		||||
 | 
			
		||||
    size_t name_len = cmd_buff[3];
 | 
			
		||||
    if (name_len > Service::kMaxPortSize) {
 | 
			
		||||
        cmd_buff[1] = ERR_INVALID_NAME_SIZE.raw;
 | 
			
		||||
        LOG_ERROR(Service_SRV, "called name_len=0x%X, failed with code=0x%08X", name_len,
 | 
			
		||||
                  cmd_buff[1]);
 | 
			
		||||
        IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
        rb.Push(ERR_INVALID_NAME_SIZE);
 | 
			
		||||
        LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    std::string name(reinterpret_cast<const char*>(&cmd_buff[1]), name_len);
 | 
			
		||||
    bool return_port_on_failure = (cmd_buff[4] & 1) == 0;
 | 
			
		||||
    std::string name(name_buf.data(), name_len);
 | 
			
		||||
 | 
			
		||||
    // TODO(yuriks): Permission checks go here
 | 
			
		||||
 | 
			
		||||
    auto client_port = service_manager->GetServicePort(name);
 | 
			
		||||
    if (client_port.Failed()) {
 | 
			
		||||
        cmd_buff[1] = client_port.Code().raw;
 | 
			
		||||
        LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(),
 | 
			
		||||
                  cmd_buff[1]);
 | 
			
		||||
        IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
        rb.Push(client_port.Code());
 | 
			
		||||
        LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(),
 | 
			
		||||
                  client_port.Code().raw);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto session = client_port.Unwrap()->Connect();
 | 
			
		||||
    cmd_buff[1] = session.Code().raw;
 | 
			
		||||
    if (session.Succeeded()) {
 | 
			
		||||
        cmd_buff[3] = Kernel::g_handle_table.Create(session.MoveFrom()).MoveFrom();
 | 
			
		||||
        LOG_DEBUG(Service_SRV, "called service=%s, session handle=0x%08X", name.c_str(),
 | 
			
		||||
                  cmd_buff[3]);
 | 
			
		||||
        LOG_DEBUG(Service_SRV, "called service=%s -> session=%u", name.c_str(),
 | 
			
		||||
                  (*session)->GetObjectId());
 | 
			
		||||
        IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
 | 
			
		||||
        rb.Push(session.Code());
 | 
			
		||||
        rb.PushObjects(session.MoveFrom());
 | 
			
		||||
    } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) {
 | 
			
		||||
        cmd_buff[1] = ERR_MAX_CONNECTIONS_REACHED.raw;
 | 
			
		||||
        cmd_buff[3] = Kernel::g_handle_table.Create(client_port.MoveFrom()).MoveFrom();
 | 
			
		||||
        LOG_WARNING(Service_SRV, "called service=%s, *port* handle=0x%08X", name.c_str(),
 | 
			
		||||
                    cmd_buff[3]);
 | 
			
		||||
        LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u",
 | 
			
		||||
                    name.c_str(), (*client_port)->GetObjectId());
 | 
			
		||||
        IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
 | 
			
		||||
        rb.Push(ERR_MAX_CONNECTIONS_REACHED);
 | 
			
		||||
        rb.PushObjects(client_port.MoveFrom());
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(),
 | 
			
		||||
                  cmd_buff[1]);
 | 
			
		||||
        LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code());
 | 
			
		||||
        IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
        rb.Push(session.Code());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -127,12 +137,11 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 *      1: ResultCode
 | 
			
		||||
 */
 | 
			
		||||
void SRV::Subscribe(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    u32* cmd_buff = ctx.CommandBuffer();
 | 
			
		||||
    IPC::RequestParser rp(ctx, 0x9, 1, 0);
 | 
			
		||||
    u32 notification_id = rp.Pop<u32>();
 | 
			
		||||
 | 
			
		||||
    u32 notification_id = cmd_buff[1];
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw;           // No error
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -146,12 +155,11 @@ void SRV::Subscribe(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 *      1: ResultCode
 | 
			
		||||
 */
 | 
			
		||||
void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    u32* cmd_buff = ctx.CommandBuffer();
 | 
			
		||||
    IPC::RequestParser rp(ctx, 0xA, 1, 0);
 | 
			
		||||
    u32 notification_id = rp.Pop<u32>();
 | 
			
		||||
 | 
			
		||||
    u32 notification_id = cmd_buff[1];
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw;           // No error
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -166,13 +174,12 @@ void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 *      1: ResultCode
 | 
			
		||||
 */
 | 
			
		||||
void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    u32* cmd_buff = ctx.CommandBuffer();
 | 
			
		||||
    IPC::RequestParser rp(ctx, 0xC, 2, 0);
 | 
			
		||||
    u32 notification_id = rp.Pop<u32>();
 | 
			
		||||
    u8 flags = rp.Pop<u8>();
 | 
			
		||||
 | 
			
		||||
    u32 notification_id = cmd_buff[1];
 | 
			
		||||
    u8 flags = cmd_buff[2] & 0xFF;
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw;           // No error
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id,
 | 
			
		||||
                flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user