| // 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 |
| |
| #ifndef ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_MESSAGE_PACKET_H_ |
| #define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_MESSAGE_PACKET_H_ |
| |
| #include <lib/user_copy/user_ptr.h> |
| #include <stdint.h> |
| #include <zircon/types.h> |
| |
| #include <cstdint> |
| |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/intrusive_single_list.h> |
| #include <ktl/unique_ptr.h> |
| #include <object/buffer_chain.h> |
| #include <object/handle.h> |
| |
| constexpr uint32_t kMaxMessageSize = 65536u; |
| constexpr uint32_t kMaxMessageHandles = 64u; |
| constexpr uint32_t kMaxIovecsCount = 8192u; |
| |
| // ensure public constants are aligned |
| static_assert(ZX_CHANNEL_MAX_MSG_BYTES == kMaxMessageSize, ""); |
| static_assert(ZX_CHANNEL_MAX_MSG_HANDLES == kMaxMessageHandles, ""); |
| static_assert(ZX_CHANNEL_MAX_MSG_IOVECS == kMaxIovecsCount, ""); |
| |
| class Handle; |
| class MessagePacket; |
| namespace internal { |
| struct MessagePacketDeleter; |
| } // namespace internal |
| |
| // Definition of a MessagePacket's specific pointer type. Message packets must |
| // be managed using this specific type of pointer, because MessagePackets have a |
| // specific custom deletion requirement. |
| using MessagePacketPtr = ktl::unique_ptr<MessagePacket, internal::MessagePacketDeleter>; |
| |
| class MessagePacket final : public fbl::DoublyLinkedListable<MessagePacketPtr> { |
| public: |
| // The number of iovecs to read and process at a time. |
| // If number of iovecs <= kIovecChunkSize, then the message buf size will be computed and the |
| // minimum required number of pages will be allocated. Otherwise, a large enough buffer for |
| // the largest possible message will be allocated. |
| static constexpr uint32_t kIovecChunkSize = 16; |
| |
| // Creates a message packet containing the provided data and space for |
| // |num_handles| handles. The handles array is uninitialized and must |
| // be completely overwritten by clients. |
| static zx_status_t Create(user_in_ptr<const char> data, uint32_t data_size, uint32_t num_handles, |
| MessagePacketPtr* msg); |
| static zx_status_t Create(user_in_ptr<const zx_channel_iovec_t> iovecs, uint32_t num_iovecs, |
| uint32_t num_handles, MessagePacketPtr* msg); |
| static zx_status_t Create(const char* data, uint32_t data_size, uint32_t num_handles, |
| MessagePacketPtr* msg); |
| |
| uint32_t data_size() const { return data_size_; } |
| |
| // Copies the packet's |data_size()| bytes to |buf|. |
| // Returns an error if |buf| points to a bad user address. |
| zx_status_t CopyDataTo(user_out_ptr<char> buf) const { |
| return buffer_chain_->CopyOut(buf, payload_offset_, data_size_); |
| } |
| |
| uint32_t num_handles() const { return num_handles_; } |
| Handle* const* handles() const { return handles_; } |
| Handle** mutable_handles() { return handles_; } |
| |
| void set_owns_handles(bool own_handles) { owns_handles_ = own_handles; } |
| |
| // zx_channel_call treats the leading bytes of the payload as |
| // a transaction id of type zx_txid_t. |
| zx_txid_t get_txid() const { |
| if (data_size_ < sizeof(zx_txid_t)) { |
| return 0; |
| } |
| // The first few bytes of the payload are a zx_txid_t. |
| void* payload_start = buffer_chain_->buffers()->front().data() + payload_offset_; |
| return *reinterpret_cast<zx_txid_t*>(payload_start); |
| } |
| |
| void set_txid(zx_txid_t txid) { |
| if (data_size_ >= sizeof(zx_txid_t)) { |
| void* payload_start = buffer_chain_->buffers()->front().data() + payload_offset_; |
| *(reinterpret_cast<zx_txid_t*>(payload_start)) = txid; |
| } |
| } |
| |
| private: |
| // A private constructor ensures that users must use the static factory |
| // Create method to create a MessagePacket. This, in turn, guarantees that |
| // when a user creates a MessagePacket, they end up with the proper |
| // MessagePacket::UPtr type for managing the message packet's life cycle. |
| MessagePacket(BufferChain* chain, uint32_t data_size, uint32_t payload_offset, |
| uint16_t num_handles, Handle** handles) |
| : buffer_chain_(chain), |
| handles_(handles), |
| data_size_(data_size), |
| payload_offset_(payload_offset), |
| num_handles_(num_handles), |
| owns_handles_(false) {} |
| |
| // A private destructor helps to make sure that only our custom deleter is |
| // ever used to destroy this object which, in turn, makes it very difficult |
| // to not properly recycle the object. |
| ~MessagePacket() { |
| DEBUG_ASSERT(!InContainer()); |
| if (owns_handles_) { |
| for (size_t ix = 0; ix != num_handles_; ++ix) { |
| // Delete the handle via HandleOwner dtor. |
| HandleOwner ho(handles_[ix]); |
| } |
| } |
| } |
| |
| friend struct internal::MessagePacketDeleter; |
| static void recycle(MessagePacket* packet); |
| |
| static zx_status_t CreateIovecBounded(user_in_ptr<const zx_channel_iovec_t> iovecs, |
| uint32_t num_iovecs, uint32_t num_handles, |
| MessagePacketPtr* msg); |
| static zx_status_t CreateIovecUnbounded(user_in_ptr<const zx_channel_iovec_t> iovecs, |
| uint32_t num_iovecs, uint32_t num_handles, |
| MessagePacketPtr* msg); |
| static zx_status_t CreateCommon(uint32_t data_size, uint32_t num_handles, MessagePacketPtr* msg); |
| |
| void set_data_size(uint32_t data_size) { data_size_ = data_size; } |
| |
| BufferChain* buffer_chain_; |
| Handle** const handles_; |
| uint32_t data_size_; |
| const uint32_t payload_offset_; |
| const uint16_t num_handles_; |
| bool owns_handles_; |
| }; |
| |
| namespace internal { |
| struct MessagePacketDeleter { |
| void operator()(MessagePacket* packet) const noexcept { MessagePacket::recycle(packet); } |
| }; |
| } // namespace internal |
| |
| #endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_MESSAGE_PACKET_H_ |