Add initial dtb support
[petitboot] / discover / boot.c
1
2 #include <assert.h>
3
4 #include <log/log.h>
5 #include <pb-protocol/pb-protocol.h>
6 #include <system/system.h>
7 #include <talloc/talloc.h>
8 #include <url/url.h>
9
10 #include "device-handler.h"
11 #include "boot.h"
12 #include "paths.h"
13 #include "resource.h"
14
15 /**
16  * kexec_load - kexec load helper.
17  * @l_image: The local image file for kexec to execute.
18  * @l_initrd: Optional local initrd file for kexec --initrd, can be NULL.
19  * @l_dtb: Optional local dtb file for kexec --dtb, can be NULL.
20  * @args: Optional command line args for kexec --append, can be NULL.
21  */
22
23 static int kexec_load(const char *l_image, const char *l_initrd,
24         const char *l_dtb, const char *args, int dry_run)
25 {
26         int result;
27         const char *argv[7];
28         const char **p;
29         char *s_initrd = NULL;
30         char *s_dtb = NULL;
31         char *s_args = NULL;
32
33         p = argv;
34         *p++ = pb_system_apps.kexec;    /* 1 */
35         *p++ = "-l";                    /* 2 */
36
37         if (l_initrd) {
38                 s_initrd = talloc_asprintf(NULL, "--initrd=%s", l_initrd);
39                 assert(s_initrd);
40                 *p++ = s_initrd;         /* 3 */
41         }
42
43         if (l_dtb) {
44                 s_dtb = talloc_asprintf(NULL, "--dtb=%s", l_dtb);
45                 assert(s_dtb);
46                 *p++ = s_dtb;            /* 4 */
47         }
48
49         if (args) {
50                 s_args = talloc_asprintf(NULL, "--append=%s", args);
51                 assert(s_args);
52                 *p++ = s_args;          /* 5 */
53         }
54
55         *p++ = l_image;                 /* 6*/
56         *p++ = NULL;                    /* 7 */
57
58         result = pb_run_cmd(argv, 1, dry_run);
59
60         if (result)
61                 pb_log("%s: failed: (%d)\n", __func__, result);
62
63         talloc_free(s_initrd);
64         talloc_free(s_dtb);
65         talloc_free(s_args);
66
67         return result;
68 }
69
70 /**
71  * kexec_reboot - Helper to boot the new kernel.
72  *
73  * Must only be called after a successful call to kexec_load().
74  */
75
76 static int kexec_reboot(int dry_run)
77 {
78         int result = 0;
79         const char *argv[4];
80         const char **p;
81
82         /* First try running shutdown.  Init scripts should run 'exec -e' */
83
84         p = argv;
85         *p++ = pb_system_apps.shutdown; /* 1 */
86         *p++ =  "-r";                   /* 2 */
87         *p++ =  "now";                  /* 3 */
88         *p++ =  NULL;                   /* 4 */
89
90         result = pb_run_cmd(argv, 1, dry_run);
91
92         /* On error, force a kexec with the -e option */
93
94         if (result) {
95                 p = argv;
96                 *p++ = pb_system_apps.kexec;    /* 1 */
97                 *p++ = "-e";                    /* 2 */
98                 *p++ = NULL;                    /* 3 */
99
100                 result = pb_run_cmd(argv, 1, 0);
101         }
102
103         if (result)
104                 pb_log("%s: failed: (%d)\n", __func__, result);
105
106         /* okay, kexec -e -f */
107         if (result) {
108                 p = argv;
109                 *p++ = pb_system_apps.kexec;    /* 1 */
110                 *p++ = "-e";                    /* 2 */
111                 *p++ = "-f";                    /* 3 */
112                 *p++ = NULL;                    /* 4 */
113
114                 result = pb_run_cmd(argv, 1, 0);
115         }
116
117         if (result)
118                 pb_log("%s: failed: (%d)\n", __func__, result);
119
120
121         return result;
122 }
123
124 static void update_status(boot_status_fn fn, void *arg, int type,
125                 char *message)
126 {
127         struct boot_status status;
128
129         status.type = type;
130         status.message = message;
131         status.progress = -1;
132         status.detail = NULL;
133
134         fn(arg, &status);
135 }
136
137 int boot(void *ctx, struct discover_boot_option *opt, struct boot_command *cmd,
138                 int dry_run, boot_status_fn status_fn, void *status_arg)
139 {
140         char *local_image, *local_initrd, *local_dtb;
141         struct pb_url *image, *initrd, *dtb;
142         unsigned int clean_image = 0;
143         unsigned int clean_initrd = 0;
144         unsigned int clean_dtb = 0;
145         char *args;
146         int result;
147
148         local_initrd = NULL;
149         image = NULL;
150         initrd = NULL;
151         dtb = NULL;
152         args = NULL;
153
154         if (cmd && cmd->boot_image_file) {
155                 image = pb_url_parse(opt, cmd->boot_image_file);
156         } else if (opt && opt->boot_image) {
157                 image = opt->boot_image->url;
158         } else {
159                 pb_log("%s: no image specified", __func__);
160                 return -1;
161         }
162
163         if (cmd && cmd->initrd_file) {
164                 initrd = pb_url_parse(opt, cmd->initrd_file);
165         } else if (opt && opt->initrd) {
166                 initrd = opt->initrd->url;
167         }
168
169         if (cmd && cmd->dtb_file) {
170                 dtb = pb_url_parse(opt, cmd->dtb_file);
171         } else if (opt && opt->dtb) {
172                 dtb = opt->dtb->url;
173         }
174
175         if (cmd && cmd->boot_args) {
176                 args = talloc_strdup(ctx, cmd->boot_args);
177         } else if (opt && opt->option->boot_args) {
178                 args = talloc_strdup(ctx, opt->option->boot_args);
179         }
180
181         result = -1;
182
183         update_status(status_fn, status_arg, BOOT_STATUS_INFO,
184                         "loading kernel");
185         local_image = load_url(NULL, image, &clean_image);
186         if (!local_image) {
187                 update_status(status_fn, status_arg, BOOT_STATUS_ERROR,
188                                 "Couldn't load kernel image");
189                 goto no_load;
190         }
191
192         if (initrd) {
193                 update_status(status_fn, status_arg, BOOT_STATUS_INFO,
194                                 "loading initrd");
195                 local_initrd = load_url(NULL, initrd, &clean_initrd);
196                 if (!local_initrd) {
197                         update_status(status_fn, status_arg, BOOT_STATUS_ERROR,
198                                         "Couldn't load initrd image");
199                         goto no_load;
200                 }
201         }
202
203         local_dtb = NULL;
204         if (dtb) {
205                 update_status(status_fn, status_arg, BOOT_STATUS_INFO,
206                                 "loading device tree");
207                 local_dtb = load_url(NULL, dtb, &clean_dtb);
208                 if (!local_dtb) {
209                         update_status(status_fn, status_arg, BOOT_STATUS_ERROR,
210                                         "Couldn't load device tree");
211                         goto no_load;
212                 }
213         }
214
215         update_status(status_fn, status_arg, BOOT_STATUS_INFO,
216                         "performing kexec_load");
217
218         result = kexec_load(local_image, local_initrd, local_dtb,
219                                 args, dry_run);
220
221         if (result) {
222                 update_status(status_fn, status_arg, BOOT_STATUS_ERROR,
223                                 "kexec load failed");
224         }
225
226 no_load:
227         if (clean_image)
228                 unlink(local_image);
229         if (clean_initrd)
230                 unlink(local_initrd);
231         if (clean_dtb)
232                 unlink(local_dtb);
233
234         talloc_free(local_image);
235         talloc_free(local_initrd);
236         talloc_free(local_dtb);
237
238         if (!result) {
239                 update_status(status_fn, status_arg, BOOT_STATUS_INFO,
240                                 "performing kexec reboot");
241
242                 result = kexec_reboot(dry_run);
243
244                 if (result) {
245                         update_status(status_fn, status_arg, BOOT_STATUS_ERROR,
246                                         "kexec reboot failed");
247                 }
248         }
249
250         return result;
251 }