mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-12 23:16:40 +00:00
ipc::Reply -> implement Pointer handling logic
This commit is contained in:
parent
a510a1138d
commit
9d57783aa8
2 changed files with 110 additions and 3 deletions
|
@ -25,6 +25,8 @@ namespace ams::kern {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline size_t PointerTransferBufferAlignment = 0x10;
|
||||||
|
|
||||||
class ReceiveList {
|
class ReceiveList {
|
||||||
private:
|
private:
|
||||||
u32 data[ipc::MessageBuffer::MessageHeader::ReceiveListCountType_CountMax * ipc::MessageBuffer::ReceiveListEntry::GetDataSize() / sizeof(u32)];
|
u32 data[ipc::MessageBuffer::MessageHeader::ReceiveListCountType_CountMax * ipc::MessageBuffer::ReceiveListEntry::GetDataSize() / sizeof(u32)];
|
||||||
|
@ -58,6 +60,60 @@ namespace ams::kern {
|
||||||
constexpr bool IsIndex() const {
|
constexpr bool IsIndex() const {
|
||||||
return this->recv_list_count > ipc::MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset;
|
return this->recv_list_count > ipc::MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetBuffer(uintptr_t &out, size_t size, int &key) const {
|
||||||
|
switch (this->recv_list_count) {
|
||||||
|
case ipc::MessageBuffer::MessageHeader::ReceiveListCountType_None:
|
||||||
|
{
|
||||||
|
out = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ipc::MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer:
|
||||||
|
{
|
||||||
|
const uintptr_t buf = util::AlignUp(this->msg_buffer_end, PointerTransferBufferAlignment);
|
||||||
|
|
||||||
|
if ((buf < buf + size) && (buf + size <= this->msg_buffer_space_end)) {
|
||||||
|
out = buf;
|
||||||
|
key = buf + size - this->msg_buffer_end;
|
||||||
|
} else {
|
||||||
|
out = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ipc::MessageBuffer::MessageHeader::ReceiveListCountType_ToSingleBuffer:
|
||||||
|
{
|
||||||
|
const ipc::MessageBuffer::ReceiveListEntry entry(this->data[0], this->data[1]);
|
||||||
|
const uintptr_t buf = util::AlignUp(entry.GetAddress() + key, PointerTransferBufferAlignment);
|
||||||
|
|
||||||
|
const uintptr_t entry_addr = entry.GetAddress();
|
||||||
|
const size_t entry_size = entry.GetSize();
|
||||||
|
|
||||||
|
if ((buf < buf + size) && (entry_addr < entry_addr + entry_size) && (buf + size <= entry_addr + entry_size)) {
|
||||||
|
out = buf;
|
||||||
|
key = buf + size - entry_addr;
|
||||||
|
} else {
|
||||||
|
out = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (key < this->recv_list_count - ipc::MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset) {
|
||||||
|
const ipc::MessageBuffer::ReceiveListEntry entry(this->data[2 * key + 0], this->data[2 * key + 1]);
|
||||||
|
|
||||||
|
const uintptr_t entry_addr = entry.GetAddress();
|
||||||
|
const size_t entry_size = entry.GetSize();
|
||||||
|
|
||||||
|
if ((entry_addr < entry_addr + entry_size) && (entry_size >= size)) {
|
||||||
|
out = entry_addr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<bool MoveHandleAllowed>
|
template<bool MoveHandleAllowed>
|
||||||
|
@ -136,6 +192,54 @@ namespace ams::kern {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Result ProcessReceiveMessagePointerDescriptors(int &offset, int &pointer_key, KProcessPageTable &dst_page_table, KProcessPageTable &src_page_table, const ipc::MessageBuffer &dst_msg, const ipc::MessageBuffer &src_msg, const ReceiveList &dst_recv_list, bool dst_user) {
|
||||||
|
/* Get the offset at the start of processing. */
|
||||||
|
const int cur_offset = offset;
|
||||||
|
|
||||||
|
/* Get the pointer desc. */
|
||||||
|
ipc::MessageBuffer::PointerDescriptor src_desc(src_msg, cur_offset);
|
||||||
|
offset += ipc::MessageBuffer::PointerDescriptor::GetDataSize() / sizeof(u32);
|
||||||
|
|
||||||
|
/* Extract address/size. */
|
||||||
|
const uintptr_t src_pointer = src_desc.GetAddress();
|
||||||
|
const size_t recv_size = src_desc.GetSize();
|
||||||
|
uintptr_t recv_pointer = 0;
|
||||||
|
|
||||||
|
/* There's nothing to do if there's no size. */
|
||||||
|
if (recv_size > 0) {
|
||||||
|
/* If using indexing, set index. */
|
||||||
|
if (dst_recv_list.IsIndex()) {
|
||||||
|
pointer_key = src_desc.GetIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the buffer. */
|
||||||
|
dst_recv_list.GetBuffer(recv_pointer, recv_size, pointer_key);
|
||||||
|
R_UNLESS(recv_pointer != 0, svc::ResultOutOfResource());
|
||||||
|
|
||||||
|
/* Perform the pointer data copy. */
|
||||||
|
if (dst_user) {
|
||||||
|
R_TRY(src_page_table.CopyMemoryFromLinearToLinearWithoutCheckDestination(dst_page_table, recv_pointer, recv_size,
|
||||||
|
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
|
||||||
|
static_cast<KMemoryPermission>(KMemoryPermission_NotMapped | KMemoryPermission_KernelReadWrite),
|
||||||
|
KMemoryAttribute_AnyLocked | KMemoryAttribute_Uncached | KMemoryAttribute_Locked, KMemoryAttribute_AnyLocked | KMemoryAttribute_Locked,
|
||||||
|
src_pointer,
|
||||||
|
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
|
||||||
|
KMemoryPermission_UserRead,
|
||||||
|
KMemoryAttribute_Uncached, KMemoryAttribute_None));
|
||||||
|
} else {
|
||||||
|
R_TRY(src_page_table.CopyMemoryFromLinearToUser(recv_pointer, recv_size, src_pointer,
|
||||||
|
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
|
||||||
|
KMemoryPermission_UserRead,
|
||||||
|
KMemoryAttribute_Uncached, KMemoryAttribute_None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the output descriptor. */
|
||||||
|
dst_msg.Set(cur_offset, ipc::MessageBuffer::PointerDescriptor(reinterpret_cast<void *>(recv_pointer), recv_size, src_desc.GetIndex()));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE Result ReceiveMessage(bool &recv_list_broken, uintptr_t dst_message_buffer, size_t dst_buffer_size, KPhysicalAddress dst_message_paddr, KThread &src_thread, uintptr_t src_message_buffer, size_t src_buffer_size, KServerSession *session, KSessionRequest *request) {
|
ALWAYS_INLINE Result ReceiveMessage(bool &recv_list_broken, uintptr_t dst_message_buffer, size_t dst_buffer_size, KPhysicalAddress dst_message_paddr, KThread &src_thread, uintptr_t src_message_buffer, size_t src_buffer_size, KServerSession *session, KSessionRequest *request) {
|
||||||
/* Prepare variables for receive. */
|
/* Prepare variables for receive. */
|
||||||
const KThread &dst_thread = GetCurrentThread();
|
const KThread &dst_thread = GetCurrentThread();
|
||||||
|
@ -233,7 +337,10 @@ namespace ams::kern {
|
||||||
|
|
||||||
/* Process any pointer buffers. */
|
/* Process any pointer buffers. */
|
||||||
for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
|
for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
|
||||||
MESOSPHERE_UNIMPLEMENTED();
|
/* After we process, make sure we track whether the receive list is broken. */
|
||||||
|
ON_SCOPE_EXIT { if (offset > dst_recv_list_idx) { recv_list_broken = true; } };
|
||||||
|
|
||||||
|
R_TRY(ProcessReceiveMessagePointerDescriptors(offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list, dst_user && dst_header.GetReceiveListCount() == ipc::MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process any map alias buffers. */
|
/* Process any map alias buffers. */
|
||||||
|
@ -271,7 +378,7 @@ namespace ams::kern {
|
||||||
if (fast_size < raw_size) {
|
if (fast_size < raw_size) {
|
||||||
R_TRY(src_page_table.CopyMemoryFromLinearToLinear(dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
|
R_TRY(src_page_table.CopyMemoryFromLinearToLinear(dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
|
||||||
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
|
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
|
||||||
static_cast<KMemoryPermission>(KMemoryPermission_NotMapped | KMemoryPermission_UserRead),
|
static_cast<KMemoryPermission>(KMemoryPermission_NotMapped | KMemoryPermission_KernelReadWrite),
|
||||||
KMemoryAttribute_AnyLocked | KMemoryAttribute_Uncached | KMemoryAttribute_Locked, KMemoryAttribute_AnyLocked | KMemoryAttribute_Locked,
|
KMemoryAttribute_AnyLocked | KMemoryAttribute_Uncached | KMemoryAttribute_Locked, KMemoryAttribute_AnyLocked | KMemoryAttribute_Locked,
|
||||||
src_message_buffer + max_fast_size,
|
src_message_buffer + max_fast_size,
|
||||||
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
|
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
|
||||||
|
|
|
@ -351,7 +351,7 @@ namespace ams::svc::ipc {
|
||||||
|
|
||||||
ALWAYS_INLINE ReceiveListEntry(u32 a, u32 b) : data{util::BitPack32{a}, util::BitPack32{b}} { /* ... */ }
|
ALWAYS_INLINE ReceiveListEntry(u32 a, u32 b) : data{util::BitPack32{a}, util::BitPack32{b}} { /* ... */ }
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE uintptr_t GetAddress() {
|
constexpr ALWAYS_INLINE uintptr_t GetAddress() const {
|
||||||
const u64 address = (static_cast<u64>(this->data[1].Get<AddressHigh>()) << AddressLow::Count) | this->data[0].Get<AddressLow>();
|
const u64 address = (static_cast<u64>(this->data[1].Get<AddressHigh>()) << AddressLow::Count) | this->data[0].Get<AddressLow>();
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue