blob: f9f33f027555b6a0fd9e9f2e91e57e7852ccf02d [file] [log] [blame]
// 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 <lib/fidl/txn_header.h>
#include <string.h>
#include <ldmsg/ldmsg.h>
// TODO(https://fxbug.dev/42072830): These handwritten encoding/decoding functions should
// be replaced with generated FIDL bindings after the linked bug about libc
// improvements.
static_assert(sizeof(ldmsg_req_t) == 1024, "Loader service requests can be at most 1024 bytes.");
zx_status_t ldmsg_req_encode(uint64_t ordinal, ldmsg_req_t* req, size_t* req_len_out,
const char* data, size_t len) {
fidl_init_txn_header(&req->header, 0, ordinal, 0);
size_t offset;
switch (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);
memset(&req->clone, 0, sizeof(req->clone));
req->clone = (ldmsg_clone_t){
.object = FIDL_HANDLE_PRESENT,
};
return ZX_OK;
case LDMSG_OP_LOAD_OBJECT:
case LDMSG_OP_CONFIG:
offset = sizeof(fidl_string_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 = (ldmsg_common_t){
.string =
{
.size = len,
.data = (char*)FIDL_ALLOC_PRESENT,
},
};
memcpy(req->data + offset, data, len);
// Make sure to zero out the extra bytes required by alignment constraints.
size_t req_len_unaligned = sizeof(fidl_message_header_t) + offset + len;
*req_len_out = FIDL_ALIGN(req_len_unaligned);
memset((char*)req + req_len_unaligned, 0, *req_len_out - req_len_unaligned);
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_CONFIG:
if ((uintptr_t)req->common.string.data != FIDL_ALLOC_PRESENT)
return ZX_ERR_INVALID_ARGS;
offset = sizeof(fidl_string_t);
break;
default:
return ZX_ERR_INVALID_ARGS;
}
size_t size = req->common.string.size;
if (LDMSG_MAX_PAYLOAD - offset - 1 < size ||
req_len != FIDL_ALIGN(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_CONFIG:
case LDMSG_OP_CLONE:
return sizeof(ldmsg_rsp_t);
case LDMSG_OP_DONE:
default:
return 0;
}
}