mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	IPC helpers
This commit is contained in:
		
							parent
							
								
									51dd13b8a0
								
							
						
					
					
						commit
						8f2b642415
					
				
					 3 changed files with 323 additions and 48 deletions
				
			
		|  | @ -206,6 +206,7 @@ set(HEADERS | ||||||
|             hle/config_mem.h |             hle/config_mem.h | ||||||
|             hle/function_wrappers.h |             hle/function_wrappers.h | ||||||
|             hle/ipc.h |             hle/ipc.h | ||||||
|  |             hle/ipc_helpers.h | ||||||
|             hle/applets/applet.h |             hle/applets/applet.h | ||||||
|             hle/applets/erreula.h |             hle/applets/erreula.h | ||||||
|             hle/applets/mii_selector.h |             hle/applets/mii_selector.h | ||||||
|  |  | ||||||
|  | @ -18,12 +18,26 @@ static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of | ||||||
|  * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to |  * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to | ||||||
|  * the service handler process' memory. |  * the service handler process' memory. | ||||||
|  * @param offset Optional offset into command buffer |  * @param offset Optional offset into command buffer | ||||||
|  |  * @param offset Optional offset into command buffer (in bytes) | ||||||
|  * @return Pointer to command buffer |  * @return Pointer to command buffer | ||||||
|  */ |  */ | ||||||
| inline u32* GetCommandBuffer(const int offset = 0) { | inline u32* GetCommandBuffer(const int offset = 0) { | ||||||
|     return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + |     return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + | ||||||
|                                     offset); |                                     offset); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static const int kStaticBuffersOffset = | ||||||
|  |     0x100; ///< Offset into static buffers, relative to command buffer header
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns a pointer to the static buffers area in the current thread's TLS | ||||||
|  |  * TODO(Subv): cf. GetCommandBuffer | ||||||
|  |  * @param offset Optional offset into static buffers area (in bytes) | ||||||
|  |  * @return Pointer to static buffers area | ||||||
|  |  */ | ||||||
|  | inline u32* GetStaticBuffers(const int offset = 0) { | ||||||
|  |     return GetCommandBuffer(kStaticBuffersOffset + offset); | ||||||
|  | } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace IPC { | namespace IPC { | ||||||
|  | @ -40,35 +54,34 @@ enum DescriptorType : u32 { | ||||||
|     CallingPid = 0x20, |     CallingPid = 0x20, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * @brief Creates a command header to be used for IPC |  | ||||||
|  * @param command_id            ID of the command to create a header for. |  | ||||||
|  * @param normal_params         Size of the normal parameters in words. Up to 63. |  | ||||||
|  * @param translate_params_size Size of the translate parameters in words. Up to 63. |  | ||||||
|  * @return The created IPC header. |  | ||||||
|  * |  | ||||||
|  * Normal parameters are sent directly to the process while the translate parameters might go |  | ||||||
|  * through modifications and checks by the kernel. |  | ||||||
|  * The translate parameters are described by headers generated with the IPC::*Desc functions. |  | ||||||
|  * |  | ||||||
|  * @note While #normal_params is equivalent to the number of normal parameters, |  | ||||||
|  * #translate_params_size includes the size occupied by the translate parameters headers. |  | ||||||
|  */ |  | ||||||
| constexpr u32 MakeHeader(u16 command_id, unsigned int normal_params, |  | ||||||
|                          unsigned int translate_params_size) { |  | ||||||
|     return (u32(command_id) << 16) | ((u32(normal_params) & 0x3F) << 6) | |  | ||||||
|            (u32(translate_params_size) & 0x3F); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| union Header { | union Header { | ||||||
|     u32 raw; |     u32 raw; | ||||||
|     BitField<0, 6, u32> translate_params_size; |     BitField<0, 6, u32> translate_params_size; | ||||||
|     BitField<6, 6, u32> normal_params; |     BitField<6, 6, u32> normal_params_size; | ||||||
|     BitField<16, 16, u32> command_id; |     BitField<16, 16, u32> command_id; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| inline Header ParseHeader(u32 header) { | /**
 | ||||||
|     return {header}; | * @brief Creates a command header to be used for IPC | ||||||
|  | * @param command_id            ID of the command to create a header for. | ||||||
|  | * @param normal_params_size         Size of the normal parameters in words. Up to 63. | ||||||
|  | * @param translate_params_size Size of the translate parameters in words. Up to 63. | ||||||
|  | * @return The created IPC header. | ||||||
|  | * | ||||||
|  | * Normal parameters are sent directly to the process while the translate parameters might go | ||||||
|  | * through modifications and checks by the kernel. | ||||||
|  | * The translate parameters are described by headers generated with the IPC::*Desc functions. | ||||||
|  | * | ||||||
|  | * @note While #normal_params_size is equivalent to the number of normal parameters, | ||||||
|  | * #translate_params_size includes the size occupied by the translate parameters headers. | ||||||
|  | */ | ||||||
|  | inline u32 MakeHeader(u16 command_id, unsigned int normal_params_size, | ||||||
|  |                       unsigned int translate_params_size) { | ||||||
|  |     Header header; | ||||||
|  |     header.command_id.Assign(command_id); | ||||||
|  |     header.normal_params_size.Assign(normal_params_size); | ||||||
|  |     header.translate_params_size.Assign(translate_params_size); | ||||||
|  |     return header.raw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| constexpr u32 MoveHandleDesc(u32 num_handles = 1) { | constexpr u32 MoveHandleDesc(u32 num_handles = 1) { | ||||||
|  | @ -83,7 +96,7 @@ constexpr u32 CallingPidDesc() { | ||||||
|     return CallingPid; |     return CallingPid; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| constexpr bool isHandleDescriptor(u32 descriptor) { | constexpr bool IsHandleDescriptor(u32 descriptor) { | ||||||
|     return (descriptor & 0xF) == 0x0; |     return (descriptor & 0xF) == 0x0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -91,30 +104,31 @@ constexpr u32 HandleNumberFromDesc(u32 handle_descriptor) { | ||||||
|     return (handle_descriptor >> 26) + 1; |     return (handle_descriptor >> 26) + 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| constexpr u32 StaticBufferDesc(u32 size, u8 buffer_id) { |  | ||||||
|     return StaticBuffer | (size << 14) | ((buffer_id & 0xF) << 10); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| union StaticBufferDescInfo { | union StaticBufferDescInfo { | ||||||
|     u32 raw; |     u32 raw; | ||||||
|  |     BitField<0, 4, u32> descriptor_type; | ||||||
|     BitField<10, 4, u32> buffer_id; |     BitField<10, 4, u32> buffer_id; | ||||||
|     BitField<14, 18, u32> size; |     BitField<14, 18, u32> size; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| inline StaticBufferDescInfo ParseStaticBufferDesc(const u32 desc) { | inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { | ||||||
|     return {desc}; |     StaticBufferDescInfo info; | ||||||
|  |     info.descriptor_type.Assign(StaticBuffer); | ||||||
|  |     info.buffer_id.Assign(buffer_id); | ||||||
|  |     info.size.Assign(size); | ||||||
|  |     return info.raw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * @brief Creates a header describing a buffer to be sent over PXI. | * @brief Creates a header describing a buffer to be sent over PXI. | ||||||
|  * @param size         Size of the buffer. Max 0x00FFFFFF. | * @param size         Size of the buffer. Max 0x00FFFFFF. | ||||||
|  * @param buffer_id    The Id of the buffer. Max 0xF. | * @param buffer_id    The Id of the buffer. Max 0xF. | ||||||
|  * @param is_read_only true if the buffer is read-only. If false, the buffer is considered to have | * @param is_read_only true if the buffer is read-only. If false, the buffer is considered to have | ||||||
|  * read-write access. | * read-write access. | ||||||
|  * @return The created PXI buffer header. | * @return The created PXI buffer header. | ||||||
|  * | * | ||||||
|  * The next value is a phys-address of a table located in the BASE memregion. | * The next value is a phys-address of a table located in the BASE memregion. | ||||||
|  */ | */ | ||||||
| inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) { | inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) { | ||||||
|     u32 type = PXIBuffer; |     u32 type = PXIBuffer; | ||||||
|     if (is_read_only) |     if (is_read_only) | ||||||
|  | @ -122,29 +136,30 @@ inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) { | ||||||
|     return type | (size << 8) | ((buffer_id & 0xF) << 4); |     return type | (size << 8) | ((buffer_id & 0xF) << 4); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| enum MappedBufferPermissions { | enum MappedBufferPermissions : u32 { | ||||||
|     R = 1, |     R = 1, | ||||||
|     W = 2, |     W = 2, | ||||||
|     RW = R | W, |     RW = R | W, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { |  | ||||||
|     return MappedBuffer | (size << 4) | (u32(perms) << 1); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| union MappedBufferDescInfo { | union MappedBufferDescInfo { | ||||||
|     u32 raw; |     u32 raw; | ||||||
|     BitField<4, 28, u32> size; |     BitField<0, 4, u32> flags; | ||||||
|     BitField<1, 2, MappedBufferPermissions> perms; |     BitField<1, 2, MappedBufferPermissions> perms; | ||||||
|  |     BitField<4, 28, u32> size; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| inline MappedBufferDescInfo ParseMappedBufferDesc(const u32 desc) { | inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { | ||||||
|     return {desc}; |     MappedBufferDescInfo info; | ||||||
|  |     info.flags.Assign(MappedBuffer); | ||||||
|  |     info.perms.Assign(perms); | ||||||
|  |     info.size.Assign(size); | ||||||
|  |     return info.raw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline DescriptorType GetDescriptorType(u32 descriptor) { | inline DescriptorType GetDescriptorType(u32 descriptor) { | ||||||
|     // Note: Those checks must be done in this order
 |     // Note: Those checks must be done in this order
 | ||||||
|     if (isHandleDescriptor(descriptor)) |     if (IsHandleDescriptor(descriptor)) | ||||||
|         return (DescriptorType)(descriptor & 0x30); |         return (DescriptorType)(descriptor & 0x30); | ||||||
| 
 | 
 | ||||||
|     // handle the fact that the following descriptors can have rights
 |     // handle the fact that the following descriptors can have rights
 | ||||||
|  |  | ||||||
							
								
								
									
										259
									
								
								src/core/hle/ipc_helpers.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								src/core/hle/ipc_helpers.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,259 @@ | ||||||
|  | // Copyright 2016 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include "core/hle/ipc.h" | ||||||
|  | #include "core/hle/kernel/kernel.h" | ||||||
|  | 
 | ||||||
|  | namespace IPC { | ||||||
|  | 
 | ||||||
|  | class RequestHelperBase { | ||||||
|  | protected: | ||||||
|  |     u32* cmdbuf; | ||||||
|  |     ptrdiff_t index = 1; | ||||||
|  |     Header header; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     RequestHelperBase(u32* command_buffer, Header command_header) | ||||||
|  |         : cmdbuf(command_buffer), header(command_header) {} | ||||||
|  | 
 | ||||||
|  |     /// Returns the total size of the request in words
 | ||||||
|  |     size_t TotalSize() const { | ||||||
|  |         return 1 /* command header */ + header.normal_params_size + header.translate_params_size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ValidateHeader() { | ||||||
|  |         DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", | ||||||
|  |                          header.raw); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class RequestBuilder : public RequestHelperBase { | ||||||
|  | public: | ||||||
|  |     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, | ||||||
|  |                          MakeHeader(command_id, normal_params_size, translate_params_size)) {} | ||||||
|  | 
 | ||||||
|  |     // Validate on destruction, as there shouldn't be any case where we don't want it
 | ||||||
|  |     ~RequestBuilder() { | ||||||
|  |         ValidateHeader(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename T> | ||||||
|  |     void Push(T value); | ||||||
|  | 
 | ||||||
|  |     template <> | ||||||
|  |     void Push(u32); | ||||||
|  | 
 | ||||||
|  |     template <typename First, class... Other> | ||||||
|  |     void Push(First first_value, const Other&... other_values) { | ||||||
|  |         Push(first_value); | ||||||
|  |         Push(other_values...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * @brief Copies the content of the given trivially copyable class to the buffer as a normal | ||||||
|  |      * param | ||||||
|  |      * @note: The input class must be correctly packed/padded to fit hardware layout. | ||||||
|  |      */ | ||||||
|  |     template <typename T> | ||||||
|  |     void PushRaw(const T& value) { | ||||||
|  |         static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable"); | ||||||
|  |         std::memcpy(cmdbuf + index, &value, sizeof(T)); | ||||||
|  |         index += (sizeof(T) + 3) / 4; // round up to word length
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // TODO : ensure that translate params are added after all regular params
 | ||||||
|  |     template <typename... H> | ||||||
|  |     void PushCopyHandles(H... handles) { | ||||||
|  |         Push(CopyHandleDesc(sizeof...(H))); | ||||||
|  |         Push(static_cast<Kernel::Handle>(handles)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename... H> | ||||||
|  |     void PushMoveHandles(H... handles) { | ||||||
|  |         Push(MoveHandleDesc(sizeof...(H))); | ||||||
|  |         Push(static_cast<Kernel::Handle>(handles)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PushCurrentPIDHandle() { | ||||||
|  |         Push(CallingPidDesc()); | ||||||
|  |         Push<u32>(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { | ||||||
|  |         Push(StaticBufferDesc(size, buffer_id)); | ||||||
|  |         Push(buffer_vaddr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms) { | ||||||
|  |         Push(MappedBufferDesc(size, perms)); | ||||||
|  |         Push(buffer_vaddr); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class RequestParser : public RequestHelperBase { | ||||||
|  | public: | ||||||
|  |     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, | ||||||
|  |                         MakeHeader(command_id, normal_params_size, translate_params_size)) {} | ||||||
|  | 
 | ||||||
|  |     RequestBuilder MakeBuilder(u32 normal_params_size, u32 translate_params_size, | ||||||
|  |                                bool validateHeader = true) { | ||||||
|  |         if (validateHeader) | ||||||
|  |             ValidateHeader(); | ||||||
|  |         Header builderHeader{ | ||||||
|  |             MakeHeader(header.command_id, normal_params_size, translate_params_size)}; | ||||||
|  |         return {cmdbuf, builderHeader}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename T> | ||||||
|  |     T Pop(); | ||||||
|  | 
 | ||||||
|  |     template <> | ||||||
|  |     u32 Pop<u32>(); | ||||||
|  | 
 | ||||||
|  |     template <typename T> | ||||||
|  |     void Pop(T& value) { | ||||||
|  |         value = Pop<T>(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename First, class... Other> | ||||||
|  |     void Pop(First& first_value, Other&... other_values) { | ||||||
|  |         first_value = Pop<First>(); | ||||||
|  |         Pop(other_values...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Kernel::Handle PopHandle() { | ||||||
|  |         const u32 handle_descriptor = Pop<u32>(); | ||||||
|  |         DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor), | ||||||
|  |                          "Tried to pop handle(s) but the descriptor is not a handle descriptor"); | ||||||
|  |         DEBUG_ASSERT_MSG(HandleNumberFromDesc(handle_descriptor) == 1, | ||||||
|  |                          "Descriptor indicates that there isn't exactly one handle"); | ||||||
|  |         return Pop<Kernel::Handle>(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename... H> | ||||||
|  |     void PopHandles(H&... handles) { | ||||||
|  |         const u32 handle_descriptor = Pop<u32>(); | ||||||
|  |         const int handles_number = sizeof...(H); | ||||||
|  |         DEBUG_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), | ||||||
|  |                          "Number of handles doesn't match the descriptor"); | ||||||
|  |         Pop(static_cast<Kernel::Handle&>(handles)...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * @brief Pops the static buffer vaddr | ||||||
|  |      * @return                  The virtual address of the buffer | ||||||
|  |      * @param[out] data_size    If non-null, the pointed value will be set to the size of the data | ||||||
|  |      * @param[out] useStaticBuffersToGetVaddr Indicates if we should read the vaddr from the static | ||||||
|  |      * buffers (which is the correct thing to do, but no service presently implement it) instead of | ||||||
|  |      * using the same value as the process who sent the request | ||||||
|  |      * given by the source process | ||||||
|  |      * | ||||||
|  |      * Static buffers must be set up before any IPC request using those is sent. | ||||||
|  |      * It is the duty of the process (usually services) to allocate and set up the receiving static | ||||||
|  |      * buffer information | ||||||
|  |      * Please note that the setup uses virtual addresses. | ||||||
|  |      */ | ||||||
|  |     VAddr PopStaticBuffer(size_t* data_size = nullptr, bool useStaticBuffersToGetVaddr = false) { | ||||||
|  |         const u32 sbuffer_descriptor = Pop<u32>(); | ||||||
|  |         StaticBufferDescInfo bufferInfo{sbuffer_descriptor}; | ||||||
|  |         if (data_size != nullptr) | ||||||
|  |             *data_size = bufferInfo.size; | ||||||
|  |         if (!useStaticBuffersToGetVaddr) | ||||||
|  |             return Pop<VAddr>(); | ||||||
|  |         else { | ||||||
|  |             ASSERT_MSG(0, "remove the assert if multiprocess/IPC translation are implemented."); | ||||||
|  |             // The buffer has already been copied to the static buffer by the kernel during
 | ||||||
|  |             // translation
 | ||||||
|  |             Pop<VAddr>(); // Pop the calling process buffer address
 | ||||||
|  |                           // and get the vaddr from the static buffers
 | ||||||
|  |             return cmdbuf[(0x100 >> 2) + bufferInfo.buffer_id * 2 + 1]; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * @brief Pops the mapped buffer vaddr | ||||||
|  |      * @return                  The virtual address of the buffer | ||||||
|  |      * @param[out] data_size    If non-null, the pointed value will be set to the size of the data | ||||||
|  |      * given by the source process | ||||||
|  |      * @param[out] buffer_perms If non-null, the pointed value will be set to the permissions of the | ||||||
|  |      * buffer | ||||||
|  |      */ | ||||||
|  |     VAddr PopMappedBuffer(size_t* data_size = nullptr, | ||||||
|  |                           MappedBufferPermissions* buffer_perms = nullptr) { | ||||||
|  |         const u32 sbuffer_descriptor = Pop<u32>(); | ||||||
|  |         MappedBufferDescInfo bufferInfo{sbuffer_descriptor}; | ||||||
|  |         if (data_size != nullptr) | ||||||
|  |             *data_size = bufferInfo.size; | ||||||
|  |         if (buffer_perms != nullptr) | ||||||
|  |             *buffer_perms = bufferInfo.perms; | ||||||
|  |         return Pop<VAddr>(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * @brief Reads the next normal parameters as a struct, by copying it | ||||||
|  |      * @note: The output class must be correctly packed/padded to fit hardware layout. | ||||||
|  |      */ | ||||||
|  |     template <typename T> | ||||||
|  |     void PopRaw(T& value) { | ||||||
|  |         static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable"); | ||||||
|  |         std::memcpy(&value, cmdbuf + index, sizeof(T)); | ||||||
|  |         index += (sizeof(T) + 3) / 4; // round up to word length
 | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// Pop ///
 | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | inline u32 RequestParser::Pop<u32>() { | ||||||
|  |     return cmdbuf[index++]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | inline u64 RequestParser::Pop<u64>() { | ||||||
|  |     const u64 lsw = Pop<u32>(); | ||||||
|  |     const u64 msw = Pop<u32>(); | ||||||
|  |     return msw << 32 | lsw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | inline ResultCode RequestParser::Pop<ResultCode>() { | ||||||
|  |     return ResultCode{Pop<u32>()}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Push ///
 | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | inline void RequestBuilder::Push<u32>(u32 value) { | ||||||
|  |     cmdbuf[index++] = value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | inline void RequestBuilder::Push<u64>(u64 value) { | ||||||
|  |     Push(static_cast<u32>(value)); | ||||||
|  |     Push(static_cast<u32>(value >> 32)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | inline void RequestBuilder::Push<ResultCode>(ResultCode value) { | ||||||
|  |     Push(value.raw); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace IPC
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue