]>
Commit | Line | Data |
---|---|---|
7a9219c1 SG |
1 | /* |
2 | * Copyright (c) 2011 The Chromium OS Authors. | |
1a459660 | 3 | * SPDX-License-Identifier: GPL-2.0+ |
7a9219c1 SG |
4 | */ |
5 | ||
62584db1 | 6 | #include <dirent.h> |
e1012472 | 7 | #include <errno.h> |
7a9219c1 | 8 | #include <fcntl.h> |
70db4212 | 9 | #include <getopt.h> |
62584db1 | 10 | #include <stdio.h> |
2a54d159 | 11 | #include <stdint.h> |
7a9219c1 | 12 | #include <stdlib.h> |
62584db1 | 13 | #include <string.h> |
ab06a758 | 14 | #include <termios.h> |
d99a6874 | 15 | #include <time.h> |
e1012472 | 16 | #include <unistd.h> |
21899b10 | 17 | #include <sys/mman.h> |
e1012472 | 18 | #include <sys/stat.h> |
3bdf56b7 | 19 | #include <sys/time.h> |
e1012472 | 20 | #include <sys/types.h> |
d99a6874 | 21 | #include <linux/types.h> |
7a9219c1 | 22 | |
70db4212 SG |
23 | #include <asm/getopt.h> |
24 | #include <asm/sections.h> | |
25 | #include <asm/state.h> | |
7a9219c1 | 26 | #include <os.h> |
94eefdee | 27 | #include <rtc_def.h> |
7a9219c1 SG |
28 | |
29 | /* Operating System Interface */ | |
30 | ||
77595c6d SG |
31 | struct os_mem_hdr { |
32 | size_t length; /* number of bytes in the block */ | |
33 | }; | |
34 | ||
7a9219c1 SG |
35 | ssize_t os_read(int fd, void *buf, size_t count) |
36 | { | |
37 | return read(fd, buf, count); | |
38 | } | |
39 | ||
e101550a TH |
40 | ssize_t os_read_no_block(int fd, void *buf, size_t count) |
41 | { | |
42 | const int flags = fcntl(fd, F_GETFL, 0); | |
43 | ||
44 | fcntl(fd, F_SETFL, flags | O_NONBLOCK); | |
45 | return os_read(fd, buf, count); | |
46 | } | |
47 | ||
7a9219c1 SG |
48 | ssize_t os_write(int fd, const void *buf, size_t count) |
49 | { | |
50 | return write(fd, buf, count); | |
51 | } | |
52 | ||
e2dcefcb MF |
53 | off_t os_lseek(int fd, off_t offset, int whence) |
54 | { | |
55 | if (whence == OS_SEEK_SET) | |
56 | whence = SEEK_SET; | |
57 | else if (whence == OS_SEEK_CUR) | |
58 | whence = SEEK_CUR; | |
59 | else if (whence == OS_SEEK_END) | |
60 | whence = SEEK_END; | |
61 | else | |
62 | os_exit(1); | |
63 | return lseek(fd, offset, whence); | |
64 | } | |
65 | ||
d9165153 | 66 | int os_open(const char *pathname, int os_flags) |
7a9219c1 | 67 | { |
d9165153 SG |
68 | int flags; |
69 | ||
70 | switch (os_flags & OS_O_MASK) { | |
71 | case OS_O_RDONLY: | |
72 | default: | |
73 | flags = O_RDONLY; | |
74 | break; | |
75 | ||
76 | case OS_O_WRONLY: | |
77 | flags = O_WRONLY; | |
78 | break; | |
79 | ||
80 | case OS_O_RDWR: | |
81 | flags = O_RDWR; | |
82 | break; | |
83 | } | |
84 | ||
85 | if (os_flags & OS_O_CREAT) | |
86 | flags |= O_CREAT; | |
87 | ||
88 | return open(pathname, flags, 0777); | |
7a9219c1 SG |
89 | } |
90 | ||
91 | int os_close(int fd) | |
92 | { | |
93 | return close(fd); | |
94 | } | |
95 | ||
cfd13e8d SW |
96 | int os_unlink(const char *pathname) |
97 | { | |
98 | return unlink(pathname); | |
99 | } | |
100 | ||
7a9219c1 SG |
101 | void os_exit(int exit_code) |
102 | { | |
103 | exit(exit_code); | |
104 | } | |
ab06a758 MF |
105 | |
106 | /* Restore tty state when we exit */ | |
107 | static struct termios orig_term; | |
ffb87905 | 108 | static bool term_setup; |
ab06a758 | 109 | |
8939df09 | 110 | void os_fd_restore(void) |
ab06a758 | 111 | { |
8939df09 | 112 | if (term_setup) { |
ffb87905 | 113 | tcsetattr(0, TCSANOW, &orig_term); |
8939df09 SG |
114 | term_setup = false; |
115 | } | |
ab06a758 MF |
116 | } |
117 | ||
118 | /* Put tty into raw mode so <tab> and <ctrl+c> work */ | |
ffb87905 | 119 | void os_tty_raw(int fd, bool allow_sigs) |
ab06a758 | 120 | { |
ab06a758 MF |
121 | struct termios term; |
122 | ||
ffb87905 | 123 | if (term_setup) |
ab06a758 | 124 | return; |
ab06a758 MF |
125 | |
126 | /* If not a tty, don't complain */ | |
127 | if (tcgetattr(fd, &orig_term)) | |
128 | return; | |
129 | ||
130 | term = orig_term; | |
131 | term.c_iflag = IGNBRK | IGNPAR; | |
132 | term.c_oflag = OPOST | ONLCR; | |
133 | term.c_cflag = CS8 | CREAD | CLOCAL; | |
ffb87905 | 134 | term.c_lflag = allow_sigs ? ISIG : 0; |
ab06a758 MF |
135 | if (tcsetattr(fd, TCSANOW, &term)) |
136 | return; | |
137 | ||
8939df09 | 138 | term_setup = true; |
ab06a758 MF |
139 | atexit(os_fd_restore); |
140 | } | |
21899b10 MW |
141 | |
142 | void *os_malloc(size_t length) | |
143 | { | |
77595c6d SG |
144 | struct os_mem_hdr *hdr; |
145 | ||
146 | hdr = mmap(NULL, length + sizeof(*hdr), PROT_READ | PROT_WRITE, | |
147 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
148 | if (hdr == MAP_FAILED) | |
149 | return NULL; | |
150 | hdr->length = length; | |
151 | ||
152 | return hdr + 1; | |
153 | } | |
154 | ||
347d06de | 155 | void os_free(void *ptr) |
77595c6d SG |
156 | { |
157 | struct os_mem_hdr *hdr = ptr; | |
158 | ||
159 | hdr--; | |
160 | if (ptr) | |
161 | munmap(hdr, hdr->length + sizeof(*hdr)); | |
162 | } | |
163 | ||
164 | void *os_realloc(void *ptr, size_t length) | |
165 | { | |
166 | struct os_mem_hdr *hdr = ptr; | |
167 | void *buf = NULL; | |
168 | ||
169 | hdr--; | |
170 | if (length != 0) { | |
171 | buf = os_malloc(length); | |
172 | if (!buf) | |
173 | return buf; | |
174 | if (ptr) { | |
175 | if (length > hdr->length) | |
176 | length = hdr->length; | |
177 | memcpy(buf, ptr, length); | |
178 | } | |
179 | } | |
180 | os_free(ptr); | |
181 | ||
182 | return buf; | |
21899b10 | 183 | } |
d99a6874 MW |
184 | |
185 | void os_usleep(unsigned long usec) | |
186 | { | |
187 | usleep(usec); | |
188 | } | |
189 | ||
2a54d159 | 190 | uint64_t __attribute__((no_instrument_function)) os_get_nsec(void) |
d99a6874 MW |
191 | { |
192 | #if defined(CLOCK_MONOTONIC) && defined(_POSIX_MONOTONIC_CLOCK) | |
193 | struct timespec tp; | |
194 | if (EINVAL == clock_gettime(CLOCK_MONOTONIC, &tp)) { | |
195 | struct timeval tv; | |
196 | ||
197 | gettimeofday(&tv, NULL); | |
198 | tp.tv_sec = tv.tv_sec; | |
199 | tp.tv_nsec = tv.tv_usec * 1000; | |
200 | } | |
201 | return tp.tv_sec * 1000000000ULL + tp.tv_nsec; | |
202 | #else | |
203 | struct timeval tv; | |
204 | gettimeofday(&tv, NULL); | |
205 | return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000; | |
206 | #endif | |
207 | } | |
70db4212 SG |
208 | |
209 | static char *short_opts; | |
210 | static struct option *long_opts; | |
211 | ||
212 | int os_parse_args(struct sandbox_state *state, int argc, char *argv[]) | |
213 | { | |
7b3efc66 | 214 | struct sandbox_cmdline_option **sb_opt = __u_boot_sandbox_option_start; |
70db4212 SG |
215 | size_t num_options = __u_boot_sandbox_option_count(); |
216 | size_t i; | |
217 | ||
218 | int hidden_short_opt; | |
219 | size_t si; | |
220 | ||
221 | int c; | |
222 | ||
223 | if (short_opts || long_opts) | |
224 | return 1; | |
225 | ||
226 | state->argc = argc; | |
227 | state->argv = argv; | |
228 | ||
229 | /* dynamically construct the arguments to the system getopt_long */ | |
230 | short_opts = os_malloc(sizeof(*short_opts) * num_options * 2 + 1); | |
231 | long_opts = os_malloc(sizeof(*long_opts) * num_options); | |
232 | if (!short_opts || !long_opts) | |
233 | return 1; | |
234 | ||
235 | /* | |
236 | * getopt_long requires "val" to be unique (since that is what the | |
237 | * func returns), so generate unique values automatically for flags | |
238 | * that don't have a short option. pick 0x100 as that is above the | |
239 | * single byte range (where ASCII/ISO-XXXX-X charsets live). | |
240 | */ | |
241 | hidden_short_opt = 0x100; | |
242 | si = 0; | |
243 | for (i = 0; i < num_options; ++i) { | |
244 | long_opts[i].name = sb_opt[i]->flag; | |
245 | long_opts[i].has_arg = sb_opt[i]->has_arg ? | |
246 | required_argument : no_argument; | |
247 | long_opts[i].flag = NULL; | |
248 | ||
249 | if (sb_opt[i]->flag_short) { | |
250 | short_opts[si++] = long_opts[i].val = sb_opt[i]->flag_short; | |
251 | if (long_opts[i].has_arg == required_argument) | |
252 | short_opts[si++] = ':'; | |
253 | } else | |
254 | long_opts[i].val = sb_opt[i]->flag_short = hidden_short_opt++; | |
255 | } | |
256 | short_opts[si] = '\0'; | |
257 | ||
258 | /* we need to handle output ourselves since u-boot provides printf */ | |
259 | opterr = 0; | |
260 | ||
261 | /* | |
262 | * walk all of the options the user gave us on the command line, | |
263 | * figure out what u-boot option structure they belong to (via | |
264 | * the unique short val key), and call the appropriate callback. | |
265 | */ | |
266 | while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { | |
267 | for (i = 0; i < num_options; ++i) { | |
268 | if (sb_opt[i]->flag_short == c) { | |
269 | if (sb_opt[i]->callback(state, optarg)) { | |
270 | state->parse_err = sb_opt[i]->flag; | |
271 | return 0; | |
272 | } | |
273 | break; | |
274 | } | |
275 | } | |
276 | if (i == num_options) { | |
277 | /* | |
278 | * store the faulting flag for later display. we have to | |
279 | * store the flag itself as the getopt parsing itself is | |
280 | * tricky: need to handle the following flags (assume all | |
281 | * of the below are unknown): | |
282 | * -a optopt='a' optind=<next> | |
283 | * -abbbb optopt='a' optind=<this> | |
284 | * -aaaaa optopt='a' optind=<this> | |
285 | * --a optopt=0 optind=<this> | |
286 | * as you can see, it is impossible to determine the exact | |
287 | * faulting flag without doing the parsing ourselves, so | |
288 | * we just report the specific flag that failed. | |
289 | */ | |
290 | if (optopt) { | |
291 | static char parse_err[3] = { '-', 0, '\0', }; | |
292 | parse_err[1] = optopt; | |
293 | state->parse_err = parse_err; | |
294 | } else | |
295 | state->parse_err = argv[optind - 1]; | |
296 | break; | |
297 | } | |
298 | } | |
299 | ||
300 | return 0; | |
301 | } | |
62584db1 SG |
302 | |
303 | void os_dirent_free(struct os_dirent_node *node) | |
304 | { | |
305 | struct os_dirent_node *next; | |
306 | ||
307 | while (node) { | |
308 | next = node->next; | |
309 | free(node); | |
310 | node = next; | |
311 | } | |
312 | } | |
313 | ||
314 | int os_dirent_ls(const char *dirname, struct os_dirent_node **headp) | |
315 | { | |
316 | struct dirent entry, *result; | |
317 | struct os_dirent_node *head, *node, *next; | |
318 | struct stat buf; | |
319 | DIR *dir; | |
320 | int ret; | |
321 | char *fname; | |
322 | int len; | |
323 | ||
324 | *headp = NULL; | |
325 | dir = opendir(dirname); | |
326 | if (!dir) | |
327 | return -1; | |
328 | ||
329 | /* Create a buffer for the maximum filename length */ | |
330 | len = sizeof(entry.d_name) + strlen(dirname) + 2; | |
331 | fname = malloc(len); | |
332 | if (!fname) { | |
333 | ret = -ENOMEM; | |
334 | goto done; | |
335 | } | |
336 | ||
337 | for (node = head = NULL;; node = next) { | |
338 | ret = readdir_r(dir, &entry, &result); | |
339 | if (ret || !result) | |
340 | break; | |
341 | next = malloc(sizeof(*node) + strlen(entry.d_name) + 1); | |
342 | if (!next) { | |
343 | os_dirent_free(head); | |
344 | ret = -ENOMEM; | |
345 | goto done; | |
346 | } | |
9c38c070 | 347 | next->next = NULL; |
62584db1 SG |
348 | strcpy(next->name, entry.d_name); |
349 | switch (entry.d_type) { | |
350 | case DT_REG: | |
351 | next->type = OS_FILET_REG; | |
352 | break; | |
353 | case DT_DIR: | |
354 | next->type = OS_FILET_DIR; | |
355 | break; | |
356 | case DT_LNK: | |
357 | next->type = OS_FILET_LNK; | |
358 | break; | |
359 | } | |
360 | next->size = 0; | |
361 | snprintf(fname, len, "%s/%s", dirname, next->name); | |
362 | if (!stat(fname, &buf)) | |
363 | next->size = buf.st_size; | |
364 | if (node) | |
365 | node->next = next; | |
366 | if (!head) | |
367 | head = node; | |
368 | } | |
369 | *headp = head; | |
370 | ||
371 | done: | |
372 | closedir(dir); | |
f80a8bbe | 373 | free(fname); |
62584db1 SG |
374 | return ret; |
375 | } | |
376 | ||
377 | const char *os_dirent_typename[OS_FILET_COUNT] = { | |
378 | " ", | |
379 | "SYM", | |
380 | "DIR", | |
381 | "???", | |
382 | }; | |
383 | ||
384 | const char *os_dirent_get_typename(enum os_dirent_t type) | |
385 | { | |
386 | if (type >= 0 && type < OS_FILET_COUNT) | |
387 | return os_dirent_typename[type]; | |
388 | ||
389 | return os_dirent_typename[OS_FILET_UNKNOWN]; | |
390 | } | |
391 | ||
96b1046d | 392 | int os_get_filesize(const char *fname, loff_t *size) |
62584db1 SG |
393 | { |
394 | struct stat buf; | |
395 | int ret; | |
396 | ||
397 | ret = stat(fname, &buf); | |
398 | if (ret) | |
399 | return ret; | |
96b1046d SR |
400 | *size = buf.st_size; |
401 | return 0; | |
62584db1 | 402 | } |
91b136c7 SG |
403 | |
404 | void os_putc(int ch) | |
405 | { | |
406 | putchar(ch); | |
407 | } | |
408 | ||
409 | void os_puts(const char *str) | |
410 | { | |
411 | while (*str) | |
412 | os_putc(*str++); | |
413 | } | |
5c2859cd SG |
414 | |
415 | int os_write_ram_buf(const char *fname) | |
416 | { | |
417 | struct sandbox_state *state = state_get_current(); | |
418 | int fd, ret; | |
419 | ||
420 | fd = open(fname, O_CREAT | O_WRONLY, 0777); | |
421 | if (fd < 0) | |
422 | return -ENOENT; | |
423 | ret = write(fd, state->ram_buf, state->ram_size); | |
424 | close(fd); | |
425 | if (ret != state->ram_size) | |
426 | return -EIO; | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
431 | int os_read_ram_buf(const char *fname) | |
432 | { | |
433 | struct sandbox_state *state = state_get_current(); | |
434 | int fd, ret; | |
96b1046d | 435 | loff_t size; |
5c2859cd | 436 | |
96b1046d SR |
437 | ret = os_get_filesize(fname, &size); |
438 | if (ret < 0) | |
439 | return ret; | |
5c2859cd SG |
440 | if (size != state->ram_size) |
441 | return -ENOSPC; | |
442 | fd = open(fname, O_RDONLY); | |
443 | if (fd < 0) | |
444 | return -ENOENT; | |
445 | ||
446 | ret = read(fd, state->ram_buf, state->ram_size); | |
447 | close(fd); | |
448 | if (ret != state->ram_size) | |
449 | return -EIO; | |
450 | ||
451 | return 0; | |
452 | } | |
47f5fcfb SG |
453 | |
454 | static int make_exec(char *fname, const void *data, int size) | |
455 | { | |
456 | int fd; | |
457 | ||
458 | strcpy(fname, "/tmp/u-boot.jump.XXXXXX"); | |
459 | fd = mkstemp(fname); | |
460 | if (fd < 0) | |
461 | return -ENOENT; | |
462 | if (write(fd, data, size) < 0) | |
463 | return -EIO; | |
464 | close(fd); | |
465 | if (chmod(fname, 0777)) | |
466 | return -ENOEXEC; | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
471 | static int add_args(char ***argvp, const char *add_args[], int count) | |
472 | { | |
473 | char **argv; | |
474 | int argc; | |
475 | ||
476 | for (argv = *argvp, argc = 0; (*argvp)[argc]; argc++) | |
477 | ; | |
478 | ||
479 | argv = malloc((argc + count + 1) * sizeof(char *)); | |
480 | if (!argv) { | |
481 | printf("Out of memory for %d argv\n", count); | |
482 | return -ENOMEM; | |
483 | } | |
484 | memcpy(argv, *argvp, argc * sizeof(char *)); | |
485 | memcpy(argv + argc, add_args, count * sizeof(char *)); | |
486 | argv[argc + count] = NULL; | |
487 | ||
488 | *argvp = argv; | |
489 | return 0; | |
490 | } | |
491 | ||
492 | int os_jump_to_image(const void *dest, int size) | |
493 | { | |
494 | struct sandbox_state *state = state_get_current(); | |
495 | char fname[30], mem_fname[30]; | |
496 | int fd, err; | |
ab839dc3 | 497 | const char *extra_args[5]; |
47f5fcfb SG |
498 | char **argv = state->argv; |
499 | #ifdef DEBUG | |
500 | int argc, i; | |
501 | #endif | |
502 | ||
503 | err = make_exec(fname, dest, size); | |
504 | if (err) | |
505 | return err; | |
506 | ||
507 | strcpy(mem_fname, "/tmp/u-boot.mem.XXXXXX"); | |
508 | fd = mkstemp(mem_fname); | |
509 | if (fd < 0) | |
510 | return -ENOENT; | |
511 | close(fd); | |
512 | err = os_write_ram_buf(mem_fname); | |
513 | if (err) | |
514 | return err; | |
515 | ||
516 | os_fd_restore(); | |
517 | ||
518 | extra_args[0] = "-j"; | |
519 | extra_args[1] = fname; | |
520 | extra_args[2] = "-m"; | |
521 | extra_args[3] = mem_fname; | |
ab839dc3 | 522 | extra_args[4] = "--rm_memory"; |
47f5fcfb SG |
523 | err = add_args(&argv, extra_args, |
524 | sizeof(extra_args) / sizeof(extra_args[0])); | |
525 | if (err) | |
526 | return err; | |
527 | ||
528 | #ifdef DEBUG | |
529 | for (i = 0; argv[i]; i++) | |
530 | printf("%d %s\n", i, argv[i]); | |
531 | #endif | |
532 | ||
533 | if (state_uninit()) | |
534 | os_exit(2); | |
535 | ||
536 | err = execv(fname, argv); | |
537 | free(argv); | |
538 | if (err) | |
539 | return err; | |
540 | ||
541 | return unlink(fname); | |
542 | } | |
94eefdee SG |
543 | |
544 | void os_localtime(struct rtc_time *rt) | |
545 | { | |
546 | time_t t = time(NULL); | |
547 | struct tm *tm; | |
548 | ||
549 | tm = localtime(&t); | |
550 | rt->tm_sec = tm->tm_sec; | |
551 | rt->tm_min = tm->tm_min; | |
552 | rt->tm_hour = tm->tm_hour; | |
553 | rt->tm_mday = tm->tm_mday; | |
554 | rt->tm_mon = tm->tm_mon + 1; | |
555 | rt->tm_year = tm->tm_year + 1900; | |
556 | rt->tm_wday = tm->tm_wday; | |
557 | rt->tm_yday = tm->tm_yday; | |
558 | rt->tm_isdst = tm->tm_isdst; | |
559 | } |