// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include static_assert(sizeof(ldmsg_req_t) == 1024, "Loader service requests can be at most 1024 bytes."); static uint64_t FidlAlign(uint32_t offset) { const uint64_t alignment_mask = FIDL_ALIGNMENT - 1; return (offset + alignment_mask) & ~alignment_mask; } zx_status_t ldmsg_req_encode(ldmsg_req_t* req, size_t* req_len_out, const char* data, size_t len) { size_t offset = 0; switch (req->header.ordinal) { case LDMSG_OP_DONE: *req_len_out = sizeof(fidl_message_header_t); return ZX_OK; case LDMSG_OP_CLONE: *req_len_out = sizeof(fidl_message_header_t) + sizeof(ldmsg_clone_t); req->clone.object = FIDL_HANDLE_PRESENT; return ZX_OK; case LDMSG_OP_LOAD_OBJECT: case LDMSG_OP_LOAD_SCRIPT_INTERPRETER: case LDMSG_OP_CONFIG: case LDMSG_OP_DEBUG_LOAD_CONFIG: offset = sizeof(fidl_string_t); break; case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK: req->common.object = FIDL_HANDLE_PRESENT; offset = sizeof(ldmsg_common_t); break; default: return ZX_ERR_INVALID_ARGS; } // Reserve one byte for the null terminator on the receiving side. if (LDMSG_MAX_PAYLOAD - offset - 1 < len) return ZX_ERR_OUT_OF_RANGE; req->common.string.size = len; req->common.string.data = (char*) FIDL_ALLOC_PRESENT; memcpy(req->data + offset, data, len); *req_len_out = FidlAlign(sizeof(fidl_message_header_t) + offset + len); return ZX_OK; } zx_status_t ldmsg_req_decode(ldmsg_req_t* req, size_t req_len, const char** data_out, size_t* len_out) { size_t offset = 0; switch (req->header.ordinal) { case LDMSG_OP_DONE: if (req_len != sizeof(fidl_message_header_t)) return ZX_ERR_INVALID_ARGS; *data_out = 0; *len_out = 0; return ZX_OK; case LDMSG_OP_CLONE: if (req_len != sizeof(fidl_message_header_t) + sizeof(ldmsg_clone_t) || req->clone.object != FIDL_HANDLE_PRESENT) return ZX_ERR_INVALID_ARGS; *data_out = 0; *len_out = 0; return ZX_OK; case LDMSG_OP_LOAD_OBJECT: case LDMSG_OP_LOAD_SCRIPT_INTERPRETER: case LDMSG_OP_CONFIG: case LDMSG_OP_DEBUG_LOAD_CONFIG: if ((uintptr_t)req->common.string.data != FIDL_ALLOC_PRESENT) return ZX_ERR_INVALID_ARGS; offset = sizeof(fidl_string_t); break; case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK: if ((uintptr_t)req->common.string.data != FIDL_ALLOC_PRESENT || req->common.object != FIDL_HANDLE_PRESENT) return ZX_ERR_INVALID_ARGS; offset = sizeof(ldmsg_common_t); break; default: return ZX_ERR_INVALID_ARGS; } size_t size = req->common.string.size; if (LDMSG_MAX_PAYLOAD - offset - 1 < size || req_len != FidlAlign(sizeof(fidl_message_header_t) + offset + size)) return ZX_ERR_INVALID_ARGS; // Null terminate the string. The message isn't required to have a null // terminated string, but we have enough space in our buffer for the null // terminator and adding it makes life easier for our caller. req->data[offset + size] = '\0'; *data_out = req->data + offset; *len_out = size; return ZX_OK; } size_t ldmsg_rsp_get_size(ldmsg_rsp_t* rsp) { switch (rsp->header.ordinal) { case LDMSG_OP_LOAD_OBJECT: case LDMSG_OP_LOAD_SCRIPT_INTERPRETER: case LDMSG_OP_DEBUG_LOAD_CONFIG: return sizeof(ldmsg_rsp_t); case LDMSG_OP_CONFIG: case LDMSG_OP_CLONE: case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK: return sizeof(ldmsg_rsp_t) - sizeof(zx_handle_t); case LDMSG_OP_DONE: default: return 0; } }