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