forked from projectara/gbsim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
manifest.c
199 lines (171 loc) · 5.28 KB
/
manifest.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
* Greybus interface manifest parsing
*
* Copyright 2014 Google Inc.
* Copyright 2014 Linaro Ltd.
*
* Provided under the three clause BSD license found in the LICENSE file.
*/
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <linux/types.h>
#include "gbsim.h"
/*
* Greybus kernel assigns hd-cport-ids to cports in the order they are present
* in manifest. To match that here, we can just use a simple counter.
*/
static uint16_t hd_cport_id_counter;
static int control_done;
static uint16_t allocate_hd_cport_id(void)
{
/*
* AP's hd_cport_id GB_SVC_CPORT_ID is reserved and must not be used for
* other protocols.
*/
if (hd_cport_id_counter == GB_SVC_CPORT_ID)
++hd_cport_id_counter;
return hd_cport_id_counter++;
}
void reset_hd_cport_id(void)
{
hd_cport_id_counter = 0;
}
/*
* Validate the given descriptor. Its reported size must fit within
* the number of bytes reamining, and it must have a recognized
* type. Check that the reported size is at least as big as what
* we expect to see. (It could be bigger, perhaps for a new version
* of the format.)
*
* Returns the number of bytes consumed by the descriptor, or a
* negative errno.
*/
static int identify_descriptor(struct greybus_descriptor *desc, size_t size)
{
struct greybus_descriptor_header *desc_header = &desc->header;
size_t expected_size;
size_t desc_size;
if (size < sizeof(*desc_header)) {
gbsim_error("manifest too small\n");
return -EINVAL; /* Must at least have header */
}
desc_size = (int)le16toh(desc_header->size);
if ((size_t)desc_size > size) {
gbsim_error("descriptor too big\n");
return -EINVAL;
}
/* Descriptor needs to at least have a header */
expected_size = sizeof(*desc_header);
switch (desc_header->type) {
case GREYBUS_TYPE_STRING:
expected_size += sizeof(struct greybus_descriptor_string);
expected_size += desc->string.length;
/* String descriptors are padded to 4 byte boundaries */
expected_size = ALIGN(expected_size);
break;
case GREYBUS_TYPE_INTERFACE:
expected_size += sizeof(struct greybus_descriptor_interface);
break;
case GREYBUS_TYPE_BUNDLE:
expected_size += sizeof(struct greybus_descriptor_bundle);
break;
case GREYBUS_TYPE_CPORT:
expected_size += sizeof(struct greybus_descriptor_cport);
/*
* Module's control protocol's node might not be present in
* manifest, and the first allocated cport should be for control
* protocol.
*/
if (!control_done &&
(le16toh(desc->cport.id) != GB_CONTROL_CPORT_ID)) {
allocate_cport(GB_CONTROL_CPORT_ID,
allocate_hd_cport_id(),
GREYBUS_PROTOCOL_CONTROL);
}
control_done = 1;
allocate_cport(le16toh(desc->cport.id), allocate_hd_cport_id(),
desc->cport.protocol_id);
break;
case GREYBUS_TYPE_INVALID:
default:
gbsim_error("invalid descriptor type (%hhu)\n", desc_header->type);
return -EINVAL;
}
if (desc_size < expected_size) {
gbsim_error("%d descriptor too small (%zu < %zu)\n",
desc_header->type, desc_size, expected_size);
return -EINVAL;
}
/* Warn if there is a size mismatch */
if (desc_size != expected_size) {
gbsim_error("%d descriptor size mismatch, expected - %zu, actual - %zu)\n",
desc_header->type, expected_size, desc_size);
}
return desc_size;
}
/*
* Parse a buffer containing a Interface manifest.
*
* If we find anything wrong with the content/format of the buffer
* we reject it.
*
* The first requirement is that the manifest's version is
* one we can parse.
*
* We make an initial pass through the buffer and identify all of
* the descriptors it contains, keeping track for each its type
* and the location size of its data in the buffer.
*
* Next we scan the descriptors, looking for a interface descriptor;
* there must be exactly one of those. When found, we record the
* information it contains, and then remove that descriptor (and any
* string descriptors it refers to) from further consideration.
*
* After that we look for the interface's bundles--there must be at
* least one of those.
*
* Returns true if parsing was successful, false otherwise.
*/
bool manifest_parse(void *data, size_t size)
{
struct greybus_manifest *manifest;
struct greybus_manifest_header *header;
struct greybus_descriptor *desc;
__u16 manifest_size;
/* we have to have at _least_ the manifest header */
if (size <= sizeof(manifest->header)) {
gbsim_error("short manifest (%zu)\n", size);
return false;
}
/* Make sure the size is right */
manifest = data;
header = &manifest->header;
manifest_size = le16toh(header->size);
if (manifest_size != size) {
gbsim_error("manifest size mismatch %zu != %hu\n",
size, manifest_size);
return false;
}
/* Validate major/minor number */
if (header->version_major > GREYBUS_VERSION_MAJOR) {
gbsim_error("manifest version too new (%hhu.%hhu > %hhu.%hhu)\n",
header->version_major, header->version_minor,
GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR);
return false;
}
/* OK, find all the descriptors */
desc = (struct greybus_descriptor *)(header + 1);
size -= sizeof(*header);
/* Reset control protocol's counter */
control_done = 0;
while (size) {
int desc_size;
desc_size = identify_descriptor(desc, size);
if (desc_size < 0)
return false;
desc = (struct greybus_descriptor *)((char *)desc + desc_size);
size -= desc_size;
}
return true;
}