1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <ldmsg/ldmsg.h>
6
7 #include <string.h>
8
9 static_assert(sizeof(ldmsg_req_t) == 1024,
10 "Loader service requests can be at most 1024 bytes.");
11
FidlAlign(uint32_t offset)12 static uint64_t FidlAlign(uint32_t offset) {
13 const uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
14 return (offset + alignment_mask) & ~alignment_mask;
15 }
16
ldmsg_req_encode(ldmsg_req_t * req,size_t * req_len_out,const char * data,size_t len)17 zx_status_t ldmsg_req_encode(ldmsg_req_t* req, size_t* req_len_out,
18 const char* data, size_t len) {
19 size_t offset = 0;
20 switch (req->header.ordinal) {
21 case LDMSG_OP_DONE:
22 *req_len_out = sizeof(fidl_message_header_t);
23 return ZX_OK;
24 case LDMSG_OP_CLONE:
25 *req_len_out = sizeof(fidl_message_header_t) + sizeof(ldmsg_clone_t);
26 req->clone.object = FIDL_HANDLE_PRESENT;
27 return ZX_OK;
28 case LDMSG_OP_LOAD_OBJECT:
29 case LDMSG_OP_LOAD_SCRIPT_INTERPRETER:
30 case LDMSG_OP_CONFIG:
31 case LDMSG_OP_DEBUG_LOAD_CONFIG:
32 offset = sizeof(fidl_string_t);
33 break;
34 case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK:
35 req->common.object = FIDL_HANDLE_PRESENT;
36 offset = sizeof(ldmsg_common_t);
37 break;
38 default:
39 return ZX_ERR_INVALID_ARGS;
40 }
41
42 // Reserve one byte for the null terminator on the receiving side.
43 if (LDMSG_MAX_PAYLOAD - offset - 1 < len)
44 return ZX_ERR_OUT_OF_RANGE;
45
46 req->common.string.size = len;
47 req->common.string.data = (char*) FIDL_ALLOC_PRESENT;
48 memcpy(req->data + offset, data, len);
49
50 *req_len_out = FidlAlign(sizeof(fidl_message_header_t) + offset + len);
51 return ZX_OK;
52 }
53
ldmsg_req_decode(ldmsg_req_t * req,size_t req_len,const char ** data_out,size_t * len_out)54 zx_status_t ldmsg_req_decode(ldmsg_req_t* req, size_t req_len,
55 const char** data_out, size_t* len_out) {
56 size_t offset = 0;
57 switch (req->header.ordinal) {
58 case LDMSG_OP_DONE:
59 if (req_len != sizeof(fidl_message_header_t))
60 return ZX_ERR_INVALID_ARGS;
61 *data_out = 0;
62 *len_out = 0;
63 return ZX_OK;
64 case LDMSG_OP_CLONE:
65 if (req_len != sizeof(fidl_message_header_t) + sizeof(ldmsg_clone_t)
66 || req->clone.object != FIDL_HANDLE_PRESENT)
67 return ZX_ERR_INVALID_ARGS;
68 *data_out = 0;
69 *len_out = 0;
70 return ZX_OK;
71 case LDMSG_OP_LOAD_OBJECT:
72 case LDMSG_OP_LOAD_SCRIPT_INTERPRETER:
73 case LDMSG_OP_CONFIG:
74 case LDMSG_OP_DEBUG_LOAD_CONFIG:
75 if ((uintptr_t)req->common.string.data != FIDL_ALLOC_PRESENT)
76 return ZX_ERR_INVALID_ARGS;
77 offset = sizeof(fidl_string_t);
78 break;
79 case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK:
80 if ((uintptr_t)req->common.string.data != FIDL_ALLOC_PRESENT
81 || req->common.object != FIDL_HANDLE_PRESENT)
82 return ZX_ERR_INVALID_ARGS;
83 offset = sizeof(ldmsg_common_t);
84 break;
85 default:
86 return ZX_ERR_INVALID_ARGS;
87 }
88
89 size_t size = req->common.string.size;
90 if (LDMSG_MAX_PAYLOAD - offset - 1 < size
91 || req_len != FidlAlign(sizeof(fidl_message_header_t) + offset + size))
92 return ZX_ERR_INVALID_ARGS;
93
94 // Null terminate the string. The message isn't required to have a null
95 // terminated string, but we have enough space in our buffer for the null
96 // terminator and adding it makes life easier for our caller.
97 req->data[offset + size] = '\0';
98
99 *data_out = req->data + offset;
100 *len_out = size;
101 return ZX_OK;
102 }
103
ldmsg_rsp_get_size(ldmsg_rsp_t * rsp)104 size_t ldmsg_rsp_get_size(ldmsg_rsp_t* rsp) {
105 switch (rsp->header.ordinal) {
106 case LDMSG_OP_LOAD_OBJECT:
107 case LDMSG_OP_LOAD_SCRIPT_INTERPRETER:
108 case LDMSG_OP_DEBUG_LOAD_CONFIG:
109 return sizeof(ldmsg_rsp_t);
110 case LDMSG_OP_CONFIG:
111 case LDMSG_OP_CLONE:
112 case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK:
113 return sizeof(ldmsg_rsp_t) - sizeof(zx_handle_t);
114 case LDMSG_OP_DONE:
115 default:
116 return 0;
117 }
118 }
119