diff options
| -rw-r--r-- | src/core/hle/kernel/k_server_session.cpp | 163 |
1 files changed, 124 insertions, 39 deletions
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index db3a07f91..f6ca3dc48 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp | |||
| @@ -37,8 +37,6 @@ constexpr inline size_t ReceiveListDataSize = | |||
| 37 | 37 | ||
| 38 | using ThreadQueueImplForKServerSessionRequest = KThreadQueue; | 38 | using ThreadQueueImplForKServerSessionRequest = KThreadQueue; |
| 39 | 39 | ||
| 40 | static thread_local Common::ScratchBuffer<u8> temp_buffer; | ||
| 41 | |||
| 42 | class ReceiveList { | 40 | class ReceiveList { |
| 43 | public: | 41 | public: |
| 44 | static constexpr int GetEntryCount(const MessageBuffer::MessageHeader& header) { | 42 | static constexpr int GetEntryCount(const MessageBuffer::MessageHeader& header) { |
| @@ -269,12 +267,20 @@ Result ProcessReceiveMessagePointerDescriptors(int& offset, int& pointer_key, | |||
| 269 | R_UNLESS(recv_pointer != 0, ResultOutOfResource); | 267 | R_UNLESS(recv_pointer != 0, ResultOutOfResource); |
| 270 | 268 | ||
| 271 | // Perform the pointer data copy. | 269 | // Perform the pointer data copy. |
| 272 | // TODO: KProcessPageTable::CopyMemoryFromHeapToHeapWithoutCheckDestination | 270 | if (dst_user) { |
| 273 | // TODO: KProcessPageTable::CopyMemoryFromLinearToUser | 271 | R_TRY(src_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination( |
| 274 | 272 | dst_page_table, recv_pointer, recv_size, KMemoryState::FlagReferenceCounted, | |
| 275 | temp_buffer.resize_destructive(recv_size); | 273 | KMemoryState::FlagReferenceCounted, |
| 276 | src_page_table.GetMemory().ReadBlock(src_pointer, temp_buffer.data(), recv_size); | 274 | KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite, |
| 277 | dst_page_table.GetMemory().WriteBlock(recv_pointer, temp_buffer.data(), recv_size); | 275 | KMemoryAttribute::Uncached | KMemoryAttribute::Locked, KMemoryAttribute::Locked, |
| 276 | src_pointer, KMemoryState::FlagLinearMapped, KMemoryState::FlagLinearMapped, | ||
| 277 | KMemoryPermission::UserRead, KMemoryAttribute::Uncached, KMemoryAttribute::None)); | ||
| 278 | } else { | ||
| 279 | R_TRY(src_page_table.CopyMemoryFromLinearToUser( | ||
| 280 | recv_pointer, recv_size, src_pointer, KMemoryState::FlagLinearMapped, | ||
| 281 | KMemoryState::FlagLinearMapped, KMemoryPermission::UserRead, | ||
| 282 | KMemoryAttribute::Uncached, KMemoryAttribute::None)); | ||
| 283 | } | ||
| 278 | } | 284 | } |
| 279 | 285 | ||
| 280 | // Set the output descriptor. | 286 | // Set the output descriptor. |
| @@ -303,21 +309,22 @@ constexpr Result GetMapAliasMemoryState(KMemoryState& out, | |||
| 303 | R_SUCCEED(); | 309 | R_SUCCEED(); |
| 304 | } | 310 | } |
| 305 | 311 | ||
| 306 | constexpr Result GetMapAliasTestStateAndAttributeMask(u32& out_state, u32& out_attr_mask, | 312 | constexpr Result GetMapAliasTestStateAndAttributeMask(KMemoryState& out_state, |
| 313 | KMemoryAttribute& out_attr_mask, | ||
| 307 | KMemoryState state) { | 314 | KMemoryState state) { |
| 308 | switch (state) { | 315 | switch (state) { |
| 309 | case KMemoryState::Ipc: | 316 | case KMemoryState::Ipc: |
| 310 | out_state = static_cast<u32>(KMemoryState::FlagCanUseIpc); | 317 | out_state = KMemoryState::FlagCanUseIpc; |
| 311 | out_attr_mask = static_cast<u32>(KMemoryAttribute::Uncached | | 318 | out_attr_mask = |
| 312 | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked); | 319 | KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked; |
| 313 | break; | 320 | break; |
| 314 | case KMemoryState::NonSecureIpc: | 321 | case KMemoryState::NonSecureIpc: |
| 315 | out_state = static_cast<u32>(KMemoryState::FlagCanUseNonSecureIpc); | 322 | out_state = KMemoryState::FlagCanUseNonSecureIpc; |
| 316 | out_attr_mask = static_cast<u32>(KMemoryAttribute::Uncached | KMemoryAttribute::Locked); | 323 | out_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; |
| 317 | break; | 324 | break; |
| 318 | case KMemoryState::NonDeviceIpc: | 325 | case KMemoryState::NonDeviceIpc: |
| 319 | out_state = static_cast<u32>(KMemoryState::FlagCanUseNonDeviceIpc); | 326 | out_state = KMemoryState::FlagCanUseNonDeviceIpc; |
| 320 | out_attr_mask = static_cast<u32>(KMemoryAttribute::Uncached | KMemoryAttribute::Locked); | 327 | out_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; |
| 321 | break; | 328 | break; |
| 322 | default: | 329 | default: |
| 323 | R_THROW(ResultInvalidCombination); | 330 | R_THROW(ResultInvalidCombination); |
| @@ -708,13 +715,48 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m | |||
| 708 | if (!dst_user && !src_user) { | 715 | if (!dst_user && !src_user) { |
| 709 | // Fast case is TLS -> TLS, do raw memcpy if we can. | 716 | // Fast case is TLS -> TLS, do raw memcpy if we can. |
| 710 | std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size); | 717 | std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size); |
| 711 | } else { | 718 | } else if (dst_user) { |
| 719 | // Determine how much fast size we can copy. | ||
| 720 | const size_t max_fast_size = std::min<size_t>(offset_words + raw_size, PageSize); | ||
| 721 | const size_t fast_size = max_fast_size - offset_words; | ||
| 722 | |||
| 723 | // Determine source state; if user buffer, we require heap, and otherwise only linear | ||
| 724 | // mapped (to enable tls use). | ||
| 725 | const auto src_state = | ||
| 726 | src_user ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped; | ||
| 727 | |||
| 728 | // Determine the source permission. User buffer should be unmapped + read, TLS should be | ||
| 729 | // user readable. | ||
| 730 | const KMemoryPermission src_perm = static_cast<KMemoryPermission>( | ||
| 731 | src_user ? KMemoryPermission::NotMapped | KMemoryPermission::KernelRead | ||
| 732 | : KMemoryPermission::UserRead); | ||
| 733 | |||
| 734 | // Perform the fast part of the copy. | ||
| 735 | R_TRY(src_page_table.CopyMemoryFromLinearToKernel( | ||
| 736 | dst_msg_ptr + offset, fast_size, src_message_buffer + offset_words, src_state, | ||
| 737 | src_state, src_perm, KMemoryAttribute::Uncached, KMemoryAttribute::None)); | ||
| 738 | |||
| 739 | // If the fast part of the copy didn't get everything, perform the slow part of the | ||
| 740 | // copy. | ||
| 741 | if (fast_size < raw_size) { | ||
| 742 | R_TRY(src_page_table.CopyMemoryFromHeapToHeap( | ||
| 743 | dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size, | ||
| 744 | KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted, | ||
| 745 | KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite, | ||
| 746 | KMemoryAttribute::Uncached | KMemoryAttribute::Locked, KMemoryAttribute::Locked, | ||
| 747 | src_message_buffer + max_fast_size, src_state, src_state, src_perm, | ||
| 748 | KMemoryAttribute::Uncached, KMemoryAttribute::None)); | ||
| 749 | } | ||
| 750 | } else /* if (src_user) */ { | ||
| 751 | // The source is a user buffer, so it should be unmapped + readable. | ||
| 752 | constexpr KMemoryPermission SourcePermission = static_cast<KMemoryPermission>( | ||
| 753 | KMemoryPermission::NotMapped | KMemoryPermission::KernelRead); | ||
| 754 | |||
| 712 | // Copy the memory. | 755 | // Copy the memory. |
| 713 | temp_buffer.resize_destructive(raw_size); | 756 | R_TRY(src_page_table.CopyMemoryFromLinearToUser( |
| 714 | src_page_table.GetMemory().ReadBlock(src_message_buffer + offset_words, | 757 | dst_message_buffer + offset_words, raw_size, src_message_buffer + offset_words, |
| 715 | temp_buffer.data(), raw_size); | 758 | KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted, |
| 716 | dst_page_table.GetMemory().WriteBlock(dst_message_buffer + offset_words, | 759 | SourcePermission, KMemoryAttribute::Uncached, KMemoryAttribute::None)); |
| 717 | temp_buffer.data(), raw_size); | ||
| 718 | } | 760 | } |
| 719 | } | 761 | } |
| 720 | 762 | ||
| @@ -731,8 +773,8 @@ Result ProcessSendMessageReceiveMapping(KProcessPageTable& src_page_table, | |||
| 731 | R_SUCCEED_IF(size == 0); | 773 | R_SUCCEED_IF(size == 0); |
| 732 | 774 | ||
| 733 | // Get the memory state and attribute mask to test. | 775 | // Get the memory state and attribute mask to test. |
| 734 | u32 test_state; | 776 | KMemoryState test_state; |
| 735 | u32 test_attr_mask; | 777 | KMemoryAttribute test_attr_mask; |
| 736 | R_TRY(GetMapAliasTestStateAndAttributeMask(test_state, test_attr_mask, src_state)); | 778 | R_TRY(GetMapAliasTestStateAndAttributeMask(test_state, test_attr_mask, src_state)); |
| 737 | 779 | ||
| 738 | // Determine buffer extents. | 780 | // Determine buffer extents. |
| @@ -749,18 +791,18 @@ Result ProcessSendMessageReceiveMapping(KProcessPageTable& src_page_table, | |||
| 749 | if (aligned_dst_start != mapping_dst_start) { | 791 | if (aligned_dst_start != mapping_dst_start) { |
| 750 | ASSERT(client_address < mapping_dst_start); | 792 | ASSERT(client_address < mapping_dst_start); |
| 751 | const size_t copy_size = std::min<size_t>(size, mapping_dst_start - client_address); | 793 | const size_t copy_size = std::min<size_t>(size, mapping_dst_start - client_address); |
| 752 | temp_buffer.resize_destructive(copy_size); | 794 | R_TRY(dst_page_table.CopyMemoryFromUserToLinear( |
| 753 | src_page_table.GetMemory().ReadBlock(client_address, temp_buffer.data(), copy_size); | 795 | client_address, copy_size, test_state, test_state, KMemoryPermission::UserReadWrite, |
| 754 | dst_page_table.GetMemory().WriteBlock(server_address, temp_buffer.data(), copy_size); | 796 | test_attr_mask, KMemoryAttribute::None, server_address)); |
| 755 | } | 797 | } |
| 756 | 798 | ||
| 757 | // If the end of the buffer is unaligned, handle that. | 799 | // If the end of the buffer is unaligned, handle that. |
| 758 | if (mapping_dst_end < aligned_dst_end && | 800 | if (mapping_dst_end < aligned_dst_end && |
| 759 | (aligned_dst_start == mapping_dst_start || aligned_dst_start < mapping_dst_end)) { | 801 | (aligned_dst_start == mapping_dst_start || aligned_dst_start < mapping_dst_end)) { |
| 760 | const size_t copy_size = client_address + size - mapping_dst_end; | 802 | const size_t copy_size = client_address + size - mapping_dst_end; |
| 761 | temp_buffer.resize_destructive(copy_size); | 803 | R_TRY(dst_page_table.CopyMemoryFromUserToLinear( |
| 762 | src_page_table.GetMemory().ReadBlock(mapping_src_end, temp_buffer.data(), copy_size); | 804 | mapping_dst_end, copy_size, test_state, test_state, KMemoryPermission::UserReadWrite, |
| 763 | dst_page_table.GetMemory().WriteBlock(mapping_dst_end, temp_buffer.data(), copy_size); | 805 | test_attr_mask, KMemoryAttribute::None, mapping_src_end)); |
| 764 | } | 806 | } |
| 765 | 807 | ||
| 766 | R_SUCCEED(); | 808 | R_SUCCEED(); |
| @@ -796,9 +838,15 @@ Result ProcessSendMessagePointerDescriptors(int& offset, int& pointer_key, | |||
| 796 | R_UNLESS(recv_pointer != 0, ResultOutOfResource); | 838 | R_UNLESS(recv_pointer != 0, ResultOutOfResource); |
| 797 | 839 | ||
| 798 | // Perform the pointer data copy. | 840 | // Perform the pointer data copy. |
| 799 | temp_buffer.resize_destructive(recv_size); | 841 | const bool dst_heap = dst_user && dst_recv_list.IsToMessageBuffer(); |
| 800 | src_page_table.GetMemory().ReadBlock(src_pointer, temp_buffer.data(), recv_size); | 842 | const auto dst_state = |
| 801 | dst_page_table.GetMemory().WriteBlock(recv_pointer, temp_buffer.data(), recv_size); | 843 | dst_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped; |
| 844 | const KMemoryPermission dst_perm = | ||
| 845 | dst_heap ? KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite | ||
| 846 | : KMemoryPermission::UserReadWrite; | ||
| 847 | R_TRY(dst_page_table.CopyMemoryFromUserToLinear( | ||
| 848 | recv_pointer, recv_size, dst_state, dst_state, dst_perm, KMemoryAttribute::Uncached, | ||
| 849 | KMemoryAttribute::None, src_pointer)); | ||
| 802 | } | 850 | } |
| 803 | 851 | ||
| 804 | // Set the output descriptor. | 852 | // Set the output descriptor. |
| @@ -964,13 +1012,50 @@ Result SendMessage(KernelCore& kernel, uint64_t src_message_buffer, size_t src_b | |||
| 964 | if (!dst_user && !src_user) { | 1012 | if (!dst_user && !src_user) { |
| 965 | // Fast case is TLS -> TLS, do raw memcpy if we can. | 1013 | // Fast case is TLS -> TLS, do raw memcpy if we can. |
| 966 | std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size); | 1014 | std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size); |
| 967 | } else { | 1015 | } else if (src_user) { |
| 1016 | // Determine how much fast size we can copy. | ||
| 1017 | const size_t max_fast_size = std::min<size_t>(offset_words + raw_size, PageSize); | ||
| 1018 | const size_t fast_size = max_fast_size - offset_words; | ||
| 1019 | |||
| 1020 | // Determine dst state; if user buffer, we require heap, and otherwise only linear | ||
| 1021 | // mapped (to enable tls use). | ||
| 1022 | const auto dst_state = | ||
| 1023 | dst_user ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped; | ||
| 1024 | |||
| 1025 | // Determine the dst permission. User buffer should be unmapped + read, TLS should | ||
| 1026 | // be user readable. | ||
| 1027 | const KMemoryPermission dst_perm = | ||
| 1028 | dst_user ? KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite | ||
| 1029 | : KMemoryPermission::UserReadWrite; | ||
| 1030 | |||
| 1031 | // Perform the fast part of the copy. | ||
| 1032 | R_TRY(dst_page_table.CopyMemoryFromKernelToLinear( | ||
| 1033 | dst_message_buffer + offset_words, fast_size, dst_state, dst_state, dst_perm, | ||
| 1034 | KMemoryAttribute::Uncached, KMemoryAttribute::None, src_msg_ptr + offset)); | ||
| 1035 | |||
| 1036 | // If the fast part of the copy didn't get everything, perform the slow part of the | ||
| 1037 | // copy. | ||
| 1038 | if (fast_size < raw_size) { | ||
| 1039 | R_TRY(dst_page_table.CopyMemoryFromHeapToHeap( | ||
| 1040 | dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size, | ||
| 1041 | dst_state, dst_state, dst_perm, KMemoryAttribute::Uncached, | ||
| 1042 | KMemoryAttribute::None, src_message_buffer + max_fast_size, | ||
| 1043 | KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted, | ||
| 1044 | KMemoryPermission::NotMapped | KMemoryPermission::KernelRead, | ||
| 1045 | KMemoryAttribute::Uncached | KMemoryAttribute::Locked, | ||
| 1046 | KMemoryAttribute::Locked)); | ||
| 1047 | } | ||
| 1048 | } else /* if (dst_user) */ { | ||
| 1049 | // The destination is a user buffer, so it should be unmapped + readable. | ||
| 1050 | constexpr KMemoryPermission DestinationPermission = | ||
| 1051 | KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; | ||
| 1052 | |||
| 968 | // Copy the memory. | 1053 | // Copy the memory. |
| 969 | temp_buffer.resize_destructive(raw_size); | 1054 | R_TRY(dst_page_table.CopyMemoryFromUserToLinear( |
| 970 | src_page_table.GetMemory().ReadBlock(src_message_buffer + offset_words, | 1055 | dst_message_buffer + offset_words, raw_size, KMemoryState::FlagReferenceCounted, |
| 971 | temp_buffer.data(), raw_size); | 1056 | KMemoryState::FlagReferenceCounted, DestinationPermission, |
| 972 | dst_page_table.GetMemory().WriteBlock(dst_message_buffer + offset_words, | 1057 | KMemoryAttribute::Uncached, KMemoryAttribute::None, |
| 973 | temp_buffer.data(), raw_size); | 1058 | src_message_buffer + offset_words)); |
| 974 | } | 1059 | } |
| 975 | } | 1060 | } |
| 976 | } | 1061 | } |