|  | // Copyright 2016 The Fuchsia Authors | 
|  | // | 
|  | // Use of this source code is governed by a MIT-style | 
|  | // license that can be found in the LICENSE file or at | 
|  | // https://opensource.org/licenses/MIT | 
|  |  | 
|  | #include "object/message_packet.h" | 
|  |  | 
|  | #include <err.h> | 
|  | #include <stdint.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <new> | 
|  |  | 
|  | #include <fbl/algorithm.h> | 
|  |  | 
|  | // MessagePackets have special allocation requirements because they can contain a variable number of | 
|  | // handles and a variable size payload. | 
|  | // | 
|  | // To reduce heap fragmentation, MessagePackets are stored in a lists of fixed size buffers | 
|  | // (BufferChains) rather than a contiguous blocks of memory.  These lists and buffers are allocated | 
|  | // from the PMM. | 
|  | // | 
|  | // The first buffer in a MessagePacket's BufferChain contains the MessagePacket object, followed by | 
|  | // its handles (if any), and finally its payload data (if any). | 
|  |  | 
|  | // The MessagePacket object, its handles and zx_txid_t must all fit in the first buffer. | 
|  | static constexpr size_t kContiguousBytes = | 
|  | sizeof(MessagePacket) + (kMaxMessageHandles * sizeof(Handle*)) + sizeof(zx_txid_t); | 
|  | static_assert(kContiguousBytes <= BufferChain::kContig, ""); | 
|  |  | 
|  | // Handles are stored just after the MessagePacket. | 
|  | static constexpr uint32_t kHandlesOffset = static_cast<uint32_t>(sizeof(MessagePacket)); | 
|  |  | 
|  | // PayloadOffset returns the offset of the data payload from the start of the first buffer. | 
|  | static inline uint32_t PayloadOffset(uint32_t num_handles) { | 
|  | // The payload comes after the handles. | 
|  | return kHandlesOffset + num_handles * static_cast<uint32_t>(sizeof(Handle*)); | 
|  | } | 
|  |  | 
|  | // Creates a MessagePacket in |msg| sufficient to hold |data_size| bytes and |num_handles|. | 
|  | // | 
|  | // Note: This method does not write the payload into the MessagePacket. | 
|  | // | 
|  | // Returns ZX_OK on success. | 
|  | // | 
|  | // static | 
|  | inline zx_status_t MessagePacket::CreateCommon(uint32_t data_size, uint32_t num_handles, | 
|  | MessagePacketPtr* msg) { | 
|  | if (unlikely(data_size > kMaxMessageSize || num_handles > kMaxMessageHandles)) { | 
|  | return ZX_ERR_OUT_OF_RANGE; | 
|  | } | 
|  |  | 
|  | const uint32_t payload_offset = PayloadOffset(num_handles); | 
|  |  | 
|  | // MessagePackets lives *inside* a list of buffers.  The first buffer holds the MessagePacket | 
|  | // object, followed by its handles (if any), and finally the payload data. | 
|  | BufferChain* chain = BufferChain::Alloc(payload_offset + data_size); | 
|  | if (unlikely(!chain)) { | 
|  | return ZX_ERR_NO_MEMORY; | 
|  | } | 
|  | DEBUG_ASSERT(!chain->buffers()->is_empty()); | 
|  |  | 
|  | char* const data = chain->buffers()->front().data(); | 
|  | Handle** const handles = reinterpret_cast<Handle**>(data + kHandlesOffset); | 
|  |  | 
|  | // Construct the MessagePacket into the first buffer. | 
|  | MessagePacket* const packet = reinterpret_cast<MessagePacket*>(data); | 
|  | static_assert(kMaxMessageHandles <= UINT16_MAX, ""); | 
|  | msg->reset(new (packet) MessagePacket(chain, data_size, payload_offset, | 
|  | static_cast<uint16_t>(num_handles), handles)); | 
|  | // The MessagePacket now owns the BufferChain and msg owns the MessagePacket. | 
|  |  | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | // static | 
|  | zx_status_t MessagePacket::Create(user_in_ptr<const char> data, uint32_t data_size, | 
|  | uint32_t num_handles, MessagePacketPtr* msg) { | 
|  | MessagePacketPtr new_msg; | 
|  | zx_status_t status = CreateCommon(data_size, num_handles, &new_msg); | 
|  | if (unlikely(status != ZX_OK)) { | 
|  | return status; | 
|  | } | 
|  | status = new_msg->buffer_chain_->CopyIn(data, PayloadOffset(num_handles), data_size); | 
|  | if (unlikely(status != ZX_OK)) { | 
|  | return status; | 
|  | } | 
|  | *msg = ktl::move(new_msg); | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | // static | 
|  | zx_status_t MessagePacket::Create(const char* data, uint32_t data_size, uint32_t num_handles, | 
|  | MessagePacketPtr* msg) { | 
|  | MessagePacketPtr new_msg; | 
|  | zx_status_t status = CreateCommon(data_size, num_handles, &new_msg); | 
|  | if (unlikely(status != ZX_OK)) { | 
|  | return status; | 
|  | } | 
|  | status = new_msg->buffer_chain_->CopyInKernel(data, PayloadOffset(num_handles), data_size); | 
|  | if (unlikely(status != ZX_OK)) { | 
|  | return status; | 
|  | } | 
|  | *msg = ktl::move(new_msg); | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | void MessagePacket::recycle(MessagePacket* packet) { | 
|  | // Grab the buffer chain for this packet | 
|  | BufferChain* chain = packet->buffer_chain_; | 
|  |  | 
|  | // Manually destruct the packet.  Do not delete it; its memory did not come | 
|  | // from new, it is contained as part of the buffer chain. | 
|  | packet->~MessagePacket(); | 
|  |  | 
|  | // Now return the buffer chain to where it came from. | 
|  | BufferChain::Free(chain); | 
|  | } |