Add initial dtb support
[petitboot] / lib / pb-protocol / pb-protocol.c
1
2 #include <assert.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <stdbool.h>
6 #include <stdint.h>
7 #include <asm/byteorder.h>
8
9 #include <talloc/talloc.h>
10 #include <list/list.h>
11 #include <log/log.h>
12
13 #include "pb-protocol.h"
14
15
16 /* Message format:
17  *
18  * 4-byte action, determines the remaining message content
19  * 4-byte total payload len
20  *   - not including action and payload len header
21  *
22  * action = 0x1: device add message
23  *  payload:
24  *   4-byte len, id
25  *   4-byte len, name
26  *   4-byte len, description
27  *   4-byte len, icon_file
28  *
29  *   4-byte option count
30  *   for each option:
31  *    4-byte len, id
32  *    4-byte len, name
33  *    4-byte len, description
34  *    4-byte len, icon_file
35  *    4-byte len, boot_image_file
36  *    4-byte len, initrd_file
37  *    4-byte len, dtb_file
38  *    4-byte len, boot_args
39  *
40  * action = 0x2: device remove message
41  *  payload:
42  *   4-byte len, id
43  *
44  * action = 0x3: boot
45  *  payload:
46  *   4-byte len, boot option id
47  *   4-byte len, boot_image_file
48  *   4-byte len, initrd_file
49  *   4-byte len, dtb_file
50  *   4-byte len, boot_args
51  *
52  */
53
54 void pb_protocol_dump_device(const struct device *dev, const char *text,
55         FILE *stream)
56 {
57         struct boot_option *opt;
58
59         fprintf(stream, "%snew dev:\n", text);
60         fprintf(stream, "%s\tid:   %s\n", text, dev->id);
61         fprintf(stream, "%s\tname: %s\n", text, dev->name);
62         fprintf(stream, "%s\tdesc: %s\n", text, dev->description);
63         fprintf(stream, "%s\ticon: %s\n", text, dev->icon_file);
64         fprintf(stream, "%s\tboot options:\n", text);
65         list_for_each_entry(&dev->boot_options, opt, list) {
66                 fprintf(stream, "%s\t\tid:   %s\n", text, opt->id);
67                 fprintf(stream, "%s\t\tname: %s\n", text, opt->name);
68                 fprintf(stream, "%s\t\tdesc: %s\n", text, opt->description);
69                 fprintf(stream, "%s\t\ticon: %s\n", text, opt->icon_file);
70                 fprintf(stream, "%s\t\tboot: %s\n", text, opt->boot_image_file);
71                 fprintf(stream, "%s\t\tinit: %s\n", text, opt->initrd_file);
72                 fprintf(stream, "%s\t\tdtb:  %s\n", text, opt->dtb_file);
73                 fprintf(stream, "%s\t\targs: %s\n", text, opt->boot_args);
74         }
75 }
76
77 int pb_protocol_device_cmp(const struct device *a, const struct device *b)
78 {
79         return !strcmp(a->id, b->id);
80 }
81
82 int pb_protocol_boot_option_cmp(const struct boot_option *a,
83         const struct boot_option *b)
84 {
85         return !strcmp(a->id, b->id);
86 }
87
88 /* Write a string into the buffer, starting at pos.
89  *
90  * Returns the total length used for the write, including length header.
91  */
92 int pb_protocol_serialise_string(char *pos, const char *str)
93 {
94         int len = 0;
95
96         if (str)
97                 len = strlen(str);
98
99         *(uint32_t *)pos = __cpu_to_be32(len);
100         pos += sizeof(uint32_t);
101
102         memcpy(pos, str, len);
103
104         return len + sizeof(uint32_t);
105 }
106
107 /* Read a string from a buffer, allocating the new string as necessary.
108  *
109  * @param[in] ctx       The talloc context to base the allocation on
110  * @param[in,out] pos   Where to start reading
111  * @param[in,out] len   The amount of data remaining in the buffer
112  * @param[out] str      Pointer to resuling string
113  * @return              zero on success, non-zero on failure
114  */
115 static int read_string(void *ctx, const char **pos, unsigned int *len,
116         char **str)
117 {
118         uint32_t str_len, read_len;
119
120         if (*len < sizeof(uint32_t))
121                 return -1;
122
123         str_len = __be32_to_cpu(*(uint32_t *)(*pos));
124         read_len = sizeof(uint32_t);
125
126         if (read_len + str_len > *len)
127                 return -1;
128
129         if (str_len == 0)
130                 *str = NULL;
131         else
132                 *str = talloc_strndup(ctx, *pos + read_len, str_len);
133
134         read_len += str_len;
135
136         /* all ok, update the caller's pointers */
137         *pos += read_len;
138         *len -= read_len;
139
140         return 0;
141 }
142
143 char *pb_protocol_deserialise_string(void *ctx,
144                 const struct pb_protocol_message *message)
145 {
146         const char *buf;
147         char *str;
148         unsigned int len;
149
150         len = message->payload_len;
151         buf = message->payload;
152
153         if (read_string(ctx, &buf, &len, &str))
154                 return NULL;
155
156         return str;
157 }
158
159 static int optional_strlen(const char *str)
160 {
161         if (!str)
162                 return 0;
163         return strlen(str);
164 }
165
166 int pb_protocol_device_len(const struct device *dev)
167 {
168         return  4 + optional_strlen(dev->id) +
169                 4 + optional_strlen(dev->name) +
170                 4 + optional_strlen(dev->description) +
171                 4 + optional_strlen(dev->icon_file);
172 }
173
174 int pb_protocol_boot_option_len(const struct boot_option *opt)
175 {
176
177         return  4 + optional_strlen(opt->device_id) +
178                 4 + optional_strlen(opt->id) +
179                 4 + optional_strlen(opt->name) +
180                 4 + optional_strlen(opt->description) +
181                 4 + optional_strlen(opt->icon_file) +
182                 4 + optional_strlen(opt->boot_image_file) +
183                 4 + optional_strlen(opt->initrd_file) +
184                 4 + optional_strlen(opt->dtb_file) +
185                 4 + optional_strlen(opt->boot_args) +
186                 sizeof(opt->is_default);
187 }
188
189 int pb_protocol_boot_len(const struct boot_command *boot)
190 {
191         return  4 + optional_strlen(boot->option_id) +
192                 4 + optional_strlen(boot->boot_image_file) +
193                 4 + optional_strlen(boot->initrd_file) +
194                 4 + optional_strlen(boot->dtb_file) +
195                 4 + optional_strlen(boot->boot_args);
196 }
197
198 int pb_protocol_boot_status_len(const struct boot_status *status)
199 {
200         return  4 +
201                 4 + optional_strlen(status->message) +
202                 4 + optional_strlen(status->detail) +
203                 4;
204 }
205
206 int pb_protocol_serialise_device(const struct device *dev,
207                 char *buf, int buf_len)
208 {
209         char *pos = buf;
210
211         pos += pb_protocol_serialise_string(pos, dev->id);
212         pos += pb_protocol_serialise_string(pos, dev->name);
213         pos += pb_protocol_serialise_string(pos, dev->description);
214         pos += pb_protocol_serialise_string(pos, dev->icon_file);
215
216         assert(pos <= buf + buf_len);
217         (void)buf_len;
218
219         return 0;
220 }
221
222 int pb_protocol_serialise_boot_option(const struct boot_option *opt,
223                 char *buf, int buf_len)
224 {
225         char *pos = buf;
226
227         pos += pb_protocol_serialise_string(pos, opt->device_id);
228         pos += pb_protocol_serialise_string(pos, opt->id);
229         pos += pb_protocol_serialise_string(pos, opt->name);
230         pos += pb_protocol_serialise_string(pos, opt->description);
231         pos += pb_protocol_serialise_string(pos, opt->icon_file);
232         pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
233         pos += pb_protocol_serialise_string(pos, opt->initrd_file);
234         pos += pb_protocol_serialise_string(pos, opt->dtb_file);
235         pos += pb_protocol_serialise_string(pos, opt->boot_args);
236
237         *(bool *)pos = opt->is_default;
238         pos += sizeof(bool);
239
240         assert(pos <= buf + buf_len);
241         (void)buf_len;
242
243         return 0;
244 }
245
246 int pb_protocol_serialise_boot_command(const struct boot_command *boot,
247                 char *buf, int buf_len)
248 {
249         char *pos = buf;
250
251         pos += pb_protocol_serialise_string(pos, boot->option_id);
252         pos += pb_protocol_serialise_string(pos, boot->boot_image_file);
253         pos += pb_protocol_serialise_string(pos, boot->initrd_file);
254         pos += pb_protocol_serialise_string(pos, boot->dtb_file);
255         pos += pb_protocol_serialise_string(pos, boot->boot_args);
256
257         assert(pos <= buf + buf_len);
258         (void)buf_len;
259
260         return 0;
261 }
262
263 int pb_protocol_serialise_boot_status(const struct boot_status *status,
264                 char *buf, int buf_len)
265 {
266         char *pos = buf;
267
268         *(uint32_t *)pos = __cpu_to_be32(status->type);
269         pos += sizeof(uint32_t);
270
271         pos += pb_protocol_serialise_string(pos, status->message);
272         pos += pb_protocol_serialise_string(pos, status->detail);
273
274         *(uint32_t *)pos = __cpu_to_be32(status->type);
275         pos += sizeof(uint32_t);
276
277         assert(pos <= buf + buf_len);
278         (void)buf_len;
279
280         return 0;
281 }
282
283 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
284 {
285         int total_len, rc;
286         char *pos;
287
288         total_len = sizeof(*message) + message->payload_len;
289
290         message->payload_len = __cpu_to_be32(message->payload_len);
291         message->action = __cpu_to_be32(message->action);
292
293         for (pos = (void *)message; total_len;) {
294                 rc = write(fd, pos, total_len);
295
296                 if (rc <= 0)
297                         break;
298
299                 total_len -= rc;
300                 pos += rc;
301         }
302
303         talloc_free(message);
304
305         if (!total_len)
306                 return 0;
307
308         pb_log("%s: failed: %s\n", __func__, strerror(errno));
309         return -1;
310 }
311
312 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
313                 enum pb_protocol_action action, int payload_len)
314 {
315         struct pb_protocol_message *message;
316
317         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
318                 pb_log("%s: payload too big %u/%u\n", __func__, payload_len,
319                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
320                 return NULL;
321         }
322
323         message = talloc_size(ctx, sizeof(*message) + payload_len);
324
325         /* we convert these to big-endian in write_message() */
326         message->action = action;
327         message->payload_len = payload_len;
328
329         return message;
330
331 }
332
333 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
334 {
335         struct pb_protocol_message *message, m;
336         int rc;
337         unsigned int len;
338
339         /* use the stack for the initial 8-byte read */
340
341         rc = read(fd, &m, sizeof(m));
342         if (rc != sizeof(m))
343                 return NULL;
344
345         m.payload_len = __be32_to_cpu(m.payload_len);
346         m.action = __be32_to_cpu(m.action);
347
348         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
349                 pb_log("%s: payload too big %u/%u\n", __func__, m.payload_len,
350                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
351                 return NULL;
352         }
353
354         message = talloc_size(ctx, sizeof(m) + m.payload_len);
355         memcpy(message, &m, sizeof(m));
356
357         for (len = 0; len < m.payload_len;) {
358                 rc = read(fd, message->payload + len, m.payload_len - len);
359
360                 if (rc <= 0) {
361                         talloc_free(message);
362                         pb_log("%s: failed (%u): %s\n", __func__, len,
363                                 strerror(errno));
364                         return NULL;
365                 }
366
367                 len += rc;
368         }
369
370         return message;
371 }
372
373
374 int pb_protocol_deserialise_device(struct device *dev,
375                 const struct pb_protocol_message *message)
376 {
377         unsigned int len;
378         const char *pos;
379         int rc = -1;
380
381         len = message->payload_len;
382         pos = message->payload;
383
384         if (read_string(dev, &pos, &len, &dev->id))
385                 goto out;
386
387         if (read_string(dev, &pos, &len, &dev->name))
388                 goto out;
389
390         if (read_string(dev, &pos, &len, &dev->description))
391                 goto out;
392
393         if (read_string(dev, &pos, &len, &dev->icon_file))
394                 goto out;
395
396         rc = 0;
397
398 out:
399         return rc;
400 }
401
402 int pb_protocol_deserialise_boot_option(struct boot_option *opt,
403                 const struct pb_protocol_message *message)
404 {
405         unsigned int len;
406         const char *pos;
407         int rc = -1;
408
409         len = message->payload_len;
410         pos = message->payload;
411
412         if (read_string(opt, &pos, &len, &opt->device_id))
413                 goto out;
414
415         if (read_string(opt, &pos, &len, &opt->id))
416                 goto out;
417
418         if (read_string(opt, &pos, &len, &opt->name))
419                 goto out;
420
421         if (read_string(opt, &pos, &len, &opt->description))
422                 goto out;
423
424         if (read_string(opt, &pos, &len, &opt->icon_file))
425                 goto out;
426
427         if (read_string(opt, &pos, &len, &opt->boot_image_file))
428                 goto out;
429
430         if (read_string(opt, &pos, &len, &opt->initrd_file))
431                 goto out;
432
433         if (read_string(opt, &pos, &len, &opt->dtb_file))
434                 goto out;
435
436         if (read_string(opt, &pos, &len, &opt->boot_args))
437                 goto out;
438
439         if (len < sizeof(bool))
440                 goto out;
441         opt->is_default = *(bool *)(pos);
442
443         rc = 0;
444
445 out:
446         return rc;
447 }
448
449 int pb_protocol_deserialise_boot_command(struct boot_command *cmd,
450                 const struct pb_protocol_message *message)
451 {
452         unsigned int len;
453         const char *pos;
454         int rc = -1;
455
456         len = message->payload_len;
457         pos = message->payload;
458
459         if (read_string(cmd, &pos, &len, &cmd->option_id))
460                 goto out;
461
462         if (read_string(cmd, &pos, &len, &cmd->boot_image_file))
463                 goto out;
464
465         if (read_string(cmd, &pos, &len, &cmd->initrd_file))
466                 goto out;
467
468         if (read_string(cmd, &pos, &len, &cmd->dtb_file))
469                 goto out;
470
471         if (read_string(cmd, &pos, &len, &cmd->boot_args))
472                 goto out;
473
474         rc = 0;
475
476 out:
477         return rc;
478 }
479
480 int pb_protocol_deserialise_boot_status(struct boot_status *status,
481                 const struct pb_protocol_message *message)
482 {
483         unsigned int len;
484         const char *pos;
485         int rc = -1;
486
487         len = message->payload_len;
488         pos = message->payload;
489
490         /* first up, the type enum... */
491         if (len < sizeof(uint32_t))
492                 goto out;
493
494         status->type = __be32_to_cpu(*(uint32_t *)(pos));
495
496         switch (status->type) {
497         case BOOT_STATUS_ERROR:
498         case BOOT_STATUS_INFO:
499                 break;
500         default:
501                 goto out;
502         }
503
504         pos += sizeof(uint32_t);
505         len -= sizeof(uint32_t);
506
507         /* message and detail strings */
508         if (read_string(status, &pos, &len, &status->message))
509                 goto out;
510
511         if (read_string(status, &pos, &len, &status->detail))
512                 goto out;
513
514         /* and finally, progress */
515         if (len < sizeof(uint32_t))
516                 goto out;
517
518         status->progress = __be32_to_cpu(*(uint32_t *)(pos));
519
520         /* clamp to 100% */
521         if (status->progress > 100)
522                 status->progress = 100;
523
524         rc = 0;
525
526 out:
527         return rc;
528 }