Add boot command structure to petitboot protocol description
[petitboot] / lib / pb-protocol / pb-protocol.c
1
2 #include <assert.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <asm/byteorder.h>
7
8 #include <talloc/talloc.h>
9 #include <list/list.h>
10 #include <log/log.h>
11
12 #include "pb-protocol.h"
13
14
15 /* Message format:
16  *
17  * 4-byte action, determines the remaining message content
18  * 4-byte total payload len
19  *   - not including action and payload len header
20  *
21  * action = 0x1: device add message
22  *  payload:
23  *   4-byte len, id
24  *   4-byte len, name
25  *   4-byte len, description
26  *   4-byte len, icon_file
27  *
28  *   4-byte option count
29  *   for each option:
30  *    4-byte len, id
31  *    4-byte len, name
32  *    4-byte len, description
33  *    4-byte len, icon_file
34  *    4-byte len, boot_image_file
35  *    4-byte len, initrd_file
36  *    4-byte len, boot_args
37  *
38  * action = 0x2: device remove message
39  *  payload:
40  *   4-byte len, id
41  *
42  * action = 0x3: boot
43  *  payload:
44  *   4-byte len, boot option id
45  *   4-byte len, boot_image_file
46  *   4-byte len, initrd_file
47  *   4-byte len, boot_args
48  *
49  */
50
51 void pb_protocol_dump_device(const struct device *dev, const char *text,
52         FILE *stream)
53 {
54         struct boot_option *opt;
55
56         fprintf(stream, "%snew dev:\n", text);
57         fprintf(stream, "%s\tid:   %s\n", text, dev->id);
58         fprintf(stream, "%s\tname: %s\n", text, dev->name);
59         fprintf(stream, "%s\tdesc: %s\n", text, dev->description);
60         fprintf(stream, "%s\ticon: %s\n", text, dev->icon_file);
61         fprintf(stream, "%s\tboot options:\n", text);
62         list_for_each_entry(&dev->boot_options, opt, list) {
63                 fprintf(stream, "%s\t\tid:   %s\n", text, opt->id);
64                 fprintf(stream, "%s\t\tname: %s\n", text, opt->name);
65                 fprintf(stream, "%s\t\tdesc: %s\n", text, opt->description);
66                 fprintf(stream, "%s\t\ticon: %s\n", text, opt->icon_file);
67                 fprintf(stream, "%s\t\tboot: %s\n", text, opt->boot_image_file);
68                 fprintf(stream, "%s\t\tinit: %s\n", text, opt->initrd_file);
69                 fprintf(stream, "%s\t\targs: %s\n", text, opt->boot_args);
70         }
71 }
72
73 int pb_protocol_device_cmp(const struct device *a, const struct device *b)
74 {
75         return !strcmp(a->id, b->id);
76 }
77
78 int pb_protocol_boot_option_cmp(const struct boot_option *a,
79         const struct boot_option *b)
80 {
81         return !strcmp(a->id, b->id);
82 }
83
84 /* Write a string into the buffer, starting at pos.
85  *
86  * Returns the total length used for the write, including length header.
87  */
88 int pb_protocol_serialise_string(char *pos, const char *str)
89 {
90         int len = 0;
91
92         if (str)
93                 len = strlen(str);
94
95         *(uint32_t *)pos = __cpu_to_be32(len);
96         pos += sizeof(uint32_t);
97
98         memcpy(pos, str, len);
99
100         return len + sizeof(uint32_t);
101 }
102
103 /* Read a string from a buffer, allocating the new string as necessary.
104  *
105  * @param[in] ctx       The talloc context to base the allocation on
106  * @param[in,out] pos   Where to start reading
107  * @param[in,out] len   The amount of data remaining in the buffer
108  * @param[out] str      Pointer to resuling string
109  * @return              zero on success, non-zero on failure
110  */
111 static int read_string(void *ctx, const char **pos, unsigned int *len,
112         char **str)
113 {
114         uint32_t str_len, read_len;
115
116         if (*len < sizeof(uint32_t))
117                 return -1;
118
119         str_len = __be32_to_cpu(*(uint32_t *)(*pos));
120         read_len = sizeof(uint32_t);
121
122         if (read_len + str_len > *len)
123                 return -1;
124
125         if (str_len == 0)
126                 *str = NULL;
127         else
128                 *str = talloc_strndup(ctx, *pos + read_len, str_len);
129
130         read_len += str_len;
131
132         /* all ok, update the caller's pointers */
133         *pos += read_len;
134         *len -= read_len;
135
136         return 0;
137 }
138
139 char *pb_protocol_deserialise_string(void *ctx,
140                 const struct pb_protocol_message *message)
141 {
142         const char *buf;
143         char *str;
144         unsigned int len;
145
146         len = message->payload_len;
147         buf = message->payload;
148
149         if (read_string(ctx, &buf, &len, &str))
150                 return NULL;
151
152         return str;
153 }
154
155 static int optional_strlen(const char *str)
156 {
157         if (!str)
158                 return 0;
159         return strlen(str);
160 }
161
162 int pb_protocol_device_len(const struct device *dev)
163 {
164         struct boot_option *opt;
165         int len;
166
167         len =   4 + optional_strlen(dev->id) +
168                 4 + optional_strlen(dev->name) +
169                 4 + optional_strlen(dev->description) +
170                 4 + optional_strlen(dev->icon_file) +
171                 4;
172
173         list_for_each_entry(&dev->boot_options, opt, list) {
174                 len +=  4 + optional_strlen(opt->id) +
175                         4 + optional_strlen(opt->name) +
176                         4 + optional_strlen(opt->description) +
177                         4 + optional_strlen(opt->icon_file) +
178                         4 + optional_strlen(opt->boot_image_file) +
179                         4 + optional_strlen(opt->initrd_file) +
180                         4 + optional_strlen(opt->boot_args);
181         }
182
183         return len;
184 }
185
186 int pb_protocol_serialise_device(const struct device *dev, char *buf, int buf_len)
187 {
188         struct boot_option *opt;
189         uint32_t n;
190         char *pos;
191
192         pos = buf;
193
194         /* construct payload into buffer */
195         pos += pb_protocol_serialise_string(pos, dev->id);
196         pos += pb_protocol_serialise_string(pos, dev->name);
197         pos += pb_protocol_serialise_string(pos, dev->description);
198         pos += pb_protocol_serialise_string(pos, dev->icon_file);
199
200         /* write option count */
201         n = 0;
202
203         list_for_each_entry(&dev->boot_options, opt, list)
204                 n++;
205
206         *(uint32_t *)pos = __cpu_to_be32(n);
207         pos += sizeof(uint32_t);
208
209         /* write each option */
210         list_for_each_entry(&dev->boot_options, opt, list) {
211                 pos += pb_protocol_serialise_string(pos, opt->id);
212                 pos += pb_protocol_serialise_string(pos, opt->name);
213                 pos += pb_protocol_serialise_string(pos, opt->description);
214                 pos += pb_protocol_serialise_string(pos, opt->icon_file);
215                 pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
216                 pos += pb_protocol_serialise_string(pos, opt->initrd_file);
217                 pos += pb_protocol_serialise_string(pos, opt->boot_args);
218         }
219
220         assert(pos <= buf + buf_len);
221         (void)buf_len;
222
223         return 0;
224 }
225
226 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
227 {
228         int total_len, rc;
229         char *pos;
230
231         total_len = sizeof(*message) + message->payload_len;
232
233         message->payload_len = __cpu_to_be32(message->payload_len);
234         message->action = __cpu_to_be32(message->action);
235
236         for (pos = (void *)message; total_len;) {
237                 rc = write(fd, pos, total_len);
238
239                 if (rc <= 0)
240                         break;
241
242                 total_len -= rc;
243                 pos += rc;
244         }
245
246         talloc_free(message);
247
248         if (!total_len)
249                 return 0;
250
251         pb_log("%s: failed: %s\n", __func__, strerror(errno));
252         return -1;
253 }
254
255 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
256                 enum pb_protocol_action action, int payload_len)
257 {
258         struct pb_protocol_message *message;
259
260         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
261                 pb_log("%s: payload too big %u/%u\n", __func__, payload_len,
262                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
263                 return NULL;
264         }
265
266         message = talloc_size(ctx, sizeof(*message) + payload_len);
267
268         /* we convert these to big-endian in write_message() */
269         message->action = action;
270         message->payload_len = payload_len;
271
272         return message;
273
274 }
275
276 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
277 {
278         struct pb_protocol_message *message, m;
279         int rc;
280         unsigned int len;
281
282         /* use the stack for the initial 8-byte read */
283
284         rc = read(fd, &m, sizeof(m));
285         if (rc != sizeof(m))
286                 return NULL;
287
288         m.payload_len = __be32_to_cpu(m.payload_len);
289         m.action = __be32_to_cpu(m.action);
290
291         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
292                 pb_log("%s: payload too big %u/%u\n", __func__, m.payload_len,
293                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
294                 return NULL;
295         }
296
297         message = talloc_size(ctx, sizeof(m) + m.payload_len);
298         memcpy(message, &m, sizeof(m));
299
300         for (len = 0; len < m.payload_len;) {
301                 rc = read(fd, message->payload + len, m.payload_len - len);
302
303                 if (rc <= 0) {
304                         talloc_free(message);
305                         pb_log("%s: failed (%u): %s\n", __func__, len,
306                                 strerror(errno));
307                         return NULL;
308                 }
309
310                 len += rc;
311         }
312
313         return message;
314 }
315
316
317 struct device *pb_protocol_deserialise_device(void *ctx,
318                 const struct pb_protocol_message *message)
319 {
320         struct device *dev;
321         const char *pos;
322         int i, n_options;
323         unsigned int len;
324
325         len = message->payload_len;
326         pos = message->payload;
327
328         dev = talloc(ctx, struct device);
329
330         if (read_string(dev, &pos, &len, &dev->id))
331                 goto out_err;
332
333         if (read_string(dev, &pos, &len, &dev->name))
334                 goto out_err;
335
336         if (read_string(dev, &pos, &len, &dev->description))
337                 goto out_err;
338
339         if (read_string(dev, &pos, &len, &dev->icon_file))
340                 goto out_err;
341
342         n_options = __be32_to_cpu(*(uint32_t *)pos);
343         pos += sizeof(uint32_t);
344
345         dev->n_options = n_options;
346
347         list_init(&dev->boot_options);
348
349         for (i = 0; i < n_options; i++) {
350                 struct boot_option *opt;
351
352                 opt = talloc(dev, struct boot_option);
353
354                 if (read_string(opt, &pos, &len, &opt->id))
355                         goto out_err;
356                 if (read_string(opt, &pos, &len, &opt->name))
357                         goto out_err;
358                 if (read_string(opt, &pos, &len,
359                                         &opt->description))
360                         goto out_err;
361                 if (read_string(opt, &pos, &len,
362                                         &opt->icon_file))
363                         goto out_err;
364                 if (read_string(opt, &pos, &len,
365                                         &opt->boot_image_file))
366                         goto out_err;
367                 if (read_string(opt, &pos, &len,
368                                         &opt->initrd_file))
369                         goto out_err;
370                 if (read_string(opt, &pos, &len,
371                                         &opt->boot_args))
372                         goto out_err;
373
374                 list_add(&dev->boot_options, &opt->list);
375         }
376
377         return dev;
378
379 out_err:
380         talloc_free(dev);
381         return NULL;
382 }