Home | History | Annotate | Download | only in cpu
      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 
    147 	hdr = mmap(NULL, length + sizeof(*hdr), PROT_READ | PROT_WRITE,
    148 		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    149 	if (hdr == MAP_FAILED)
    150 		return NULL;
    151 	hdr->length = length;
    152 
    153 	return hdr + 1;
    154 }
    155 
    156 void os_free(void *ptr)
    157 {
    158 	struct os_mem_hdr *hdr = ptr;
    159 
    160 	hdr--;
    161 	if (ptr)
    162 		munmap(hdr, hdr->length + sizeof(*hdr));
    163 }
    164 
    165 void *os_realloc(void *ptr, size_t length)
    166 {
    167 	struct os_mem_hdr *hdr = ptr;
    168 	void *buf = NULL;
    169 
    170 	hdr--;
    171 	if (length != 0) {
    172 		buf = os_malloc(length);
    173 		if (!buf)
    174 			return buf;
    175 		if (ptr) {
    176 			if (length > hdr->length)
    177 				length = hdr->length;
    178 			memcpy(buf, ptr, length);
    179 		}
    180 	}
    181 	os_free(ptr);
    182 
    183 	return buf;
    184 }
    185 
    186 void os_usleep(unsigned long usec)
    187 {
    188 	usleep(usec);
    189 }
    190 
    191 uint64_t __attribute__((no_instrument_function)) os_get_nsec(void)
    192 {
    193 #if defined(CLOCK_MONOTONIC) && defined(_POSIX_MONOTONIC_CLOCK)
    194 	struct timespec tp;
    195 	if (EINVAL == clock_gettime(CLOCK_MONOTONIC, &tp)) {
    196 		struct timeval tv;
    197 
    198 		gettimeofday(&tv, NULL);
    199 		tp.tv_sec = tv.tv_sec;
    200 		tp.tv_nsec = tv.tv_usec * 1000;
    201 	}
    202 	return tp.tv_sec * 1000000000ULL + tp.tv_nsec;
    203 #else
    204 	struct timeval tv;
    205 	gettimeofday(&tv, NULL);
    206 	return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
    207 #endif
    208 }
    209 
    210 static char *short_opts;
    211 static struct option *long_opts;
    212 
    213 int os_parse_args(struct sandbox_state *state, int argc, char *argv[])
    214 {
    215 	struct sandbox_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
    216 	size_t num_options = __u_boot_sandbox_option_count();
    217 	size_t i;
    218 
    219 	int hidden_short_opt;
    220 	size_t si;
    221 
    222 	int c;
    223 
    224 	if (short_opts || long_opts)
    225 		return 1;
    226 
    227 	state->argc = argc;
    228 	state->argv = argv;
    229 
    230 	/* dynamically construct the arguments to the system getopt_long */
    231 	short_opts = os_malloc(sizeof(*short_opts) * num_options * 2 + 1);
    232 	long_opts = os_malloc(sizeof(*long_opts) * num_options);
    233 	if (!short_opts || !long_opts)
    234 		return 1;
    235 
    236 	/*
    237 	 * getopt_long requires "val" to be unique (since that is what the
    238 	 * func returns), so generate unique values automatically for flags
    239 	 * that don't have a short option.  pick 0x100 as that is above the
    240 	 * single byte range (where ASCII/ISO-XXXX-X charsets live).
    241 	 */
    242 	hidden_short_opt = 0x100;
    243 	si = 0;
    244 	for (i = 0; i < num_options; ++i) {
    245 		long_opts[i].name = sb_opt[i]->flag;
    246 		long_opts[i].has_arg = sb_opt[i]->has_arg ?
    247 			required_argument : no_argument;
    248 		long_opts[i].flag = NULL;
    249 
    250 		if (sb_opt[i]->flag_short) {
    251 			short_opts[si++] = long_opts[i].val = sb_opt[i]->flag_short;
    252 			if (long_opts[i].has_arg == required_argument)
    253 				short_opts[si++] = ':';
    254 		} else
    255 			long_opts[i].val = sb_opt[i]->flag_short = hidden_short_opt++;
    256 	}
    257 	short_opts[si] = '\0';
    258 
    259 	/* we need to handle output ourselves since u-boot provides printf */
    260 	opterr = 0;
    261 
    262 	/*
    263 	 * walk all of the options the user gave us on the command line,
    264 	 * figure out what u-boot option structure they belong to (via
    265 	 * the unique short val key), and call the appropriate callback.
    266 	 */
    267 	while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
    268 		for (i = 0; i < num_options; ++i) {
    269 			if (sb_opt[i]->flag_short == c) {
    270 				if (sb_opt[i]->callback(state, optarg)) {
    271 					state->parse_err = sb_opt[i]->flag;
    272 					return 0;
    273 				}
    274 				break;
    275 			}
    276 		}
    277 		if (i == num_options) {
    278 			/*
    279 			 * store the faulting flag for later display.  we have to
    280 			 * store the flag itself as the getopt parsing itself is
    281 			 * tricky: need to handle the following flags (assume all
    282 			 * of the below are unknown):
    283 			 *   -a        optopt='a' optind=<next>
    284 			 *   -abbbb    optopt='a' optind=<this>
    285 			 *   -aaaaa    optopt='a' optind=<this>
    286 			 *   --a       optopt=0   optind=<this>
    287 			 * as you can see, it is impossible to determine the exact
    288 			 * faulting flag without doing the parsing ourselves, so
    289 			 * we just report the specific flag that failed.
    290 			 */
    291 			if (optopt) {
    292 				static char parse_err[3] = { '-', 0, '\0', };
    293 				parse_err[1] = optopt;
    294 				state->parse_err = parse_err;
    295 			} else
    296 				state->parse_err = argv[optind - 1];
    297 			break;
    298 		}
    299 	}
    300 
    301 	return 0;
    302 }
    303 
    304 void os_dirent_free(struct os_dirent_node *node)
    305 {
    306 	struct os_dirent_node *next;
    307 
    308 	while (node) {
    309 		next = node->next;
    310 		free(node);
    311 		node = next;
    312 	}
    313 }
    314 
    315 int os_dirent_ls(const char *dirname, struct os_dirent_node **headp)
    316 {
    317 	struct dirent *entry;
    318 	struct os_dirent_node *head, *node, *next;
    319 	struct stat buf;
    320 	DIR *dir;
    321 	int ret;
    322 	char *fname;
    323 	char *old_fname;
    324 	int len;
    325 	int dirlen;
    326 
    327 	*headp = NULL;
    328 	dir = opendir(dirname);
    329 	if (!dir)
    330 		return -1;
    331 
    332 	/* Create a buffer upfront, with typically sufficient size */
    333 	dirlen = strlen(dirname) + 2;
    334 	len = dirlen + 256;
    335 	fname = malloc(len);
    336 	if (!fname) {
    337 		ret = -ENOMEM;
    338 		goto done;
    339 	}
    340 
    341 	for (node = head = NULL;; node = next) {
    342 		errno = 0;
    343 		entry = readdir(dir);
    344 		if (!entry) {
    345 			ret = errno;
    346 			break;
    347 		}
    348 		next = malloc(sizeof(*node) + strlen(entry->d_name) + 1);
    349 		if (!next) {
    350 			os_dirent_free(head);
    351 			ret = -ENOMEM;
    352 			goto done;
    353 		}
    354 		if (dirlen + strlen(entry->d_name) > len) {
    355 			len = dirlen + strlen(entry->d_name);
    356 			old_fname = fname;
    357 			fname = realloc(fname, len);
    358 			if (!fname) {
    359 				free(old_fname);
    360 				free(next);
    361 				os_dirent_free(head);
    362 				ret = -ENOMEM;
    363 				goto done;
    364 			}
    365 		}
    366 		next->next = NULL;
    367 		strcpy(next->name, entry->d_name);
    368 		switch (entry->d_type) {
    369 		case DT_REG:
    370 			next->type = OS_FILET_REG;
    371 			break;
    372 		case DT_DIR:
    373 			next->type = OS_FILET_DIR;
    374 			break;
    375 		case DT_LNK:
    376 			next->type = OS_FILET_LNK;
    377 			break;
    378 		default:
    379 			next->type = OS_FILET_UNKNOWN;
    380 		}
    381 		next->size = 0;
    382 		snprintf(fname, len, "%s/%s", dirname, next->name);
    383 		if (!stat(fname, &buf))
    384 			next->size = buf.st_size;
    385 		if (node)
    386 			node->next = next;
    387 		else
    388 			head = next;
    389 	}
    390 	*headp = head;
    391 
    392 done:
    393 	closedir(dir);
    394 	free(fname);
    395 	return ret;
    396 }
    397 
    398 const char *os_dirent_typename[OS_FILET_COUNT] = {
    399 	"   ",
    400 	"SYM",
    401 	"DIR",
    402 	"???",
    403 };
    404 
    405 const char *os_dirent_get_typename(enum os_dirent_t type)
    406 {
    407 	if (type >= OS_FILET_REG && type < OS_FILET_COUNT)
    408 		return os_dirent_typename[type];
    409 
    410 	return os_dirent_typename[OS_FILET_UNKNOWN];
    411 }
    412 
    413 int os_get_filesize(const char *fname, loff_t *size)
    414 {
    415 	struct stat buf;
    416 	int ret;
    417 
    418 	ret = stat(fname, &buf);
    419 	if (ret)
    420 		return ret;
    421 	*size = buf.st_size;
    422 	return 0;
    423 }
    424 
    425 void os_putc(int ch)
    426 {
    427 	putchar(ch);
    428 }
    429 
    430 void os_puts(const char *str)
    431 {
    432 	while (*str)
    433 		os_putc(*str++);
    434 }
    435 
    436 int os_write_ram_buf(const char *fname)
    437 {
    438 	struct sandbox_state *state = state_get_current();
    439 	int fd, ret;
    440 
    441 	fd = open(fname, O_CREAT | O_WRONLY, 0777);
    442 	if (fd < 0)
    443 		return -ENOENT;
    444 	ret = write(fd, state->ram_buf, state->ram_size);
    445 	close(fd);
    446 	if (ret != state->ram_size)
    447 		return -EIO;
    448 
    449 	return 0;
    450 }
    451 
    452 int os_read_ram_buf(const char *fname)
    453 {
    454 	struct sandbox_state *state = state_get_current();
    455 	int fd, ret;
    456 	loff_t size;
    457 
    458 	ret = os_get_filesize(fname, &size);
    459 	if (ret < 0)
    460 		return ret;
    461 	if (size != state->ram_size)
    462 		return -ENOSPC;
    463 	fd = open(fname, O_RDONLY);
    464 	if (fd < 0)
    465 		return -ENOENT;
    466 
    467 	ret = read(fd, state->ram_buf, state->ram_size);
    468 	close(fd);
    469 	if (ret != state->ram_size)
    470 		return -EIO;
    471 
    472 	return 0;
    473 }
    474 
    475 static int make_exec(char *fname, const void *data, int size)
    476 {
    477 	int fd;
    478 
    479 	strcpy(fname, "/tmp/u-boot.jump.XXXXXX");
    480 	fd = mkstemp(fname);
    481 	if (fd < 0)
    482 		return -ENOENT;
    483 	if (write(fd, data, size) < 0)
    484 		return -EIO;
    485 	close(fd);
    486 	if (chmod(fname, 0777))
    487 		return -ENOEXEC;
    488 
    489 	return 0;
    490 }
    491 
    492 static int add_args(char ***argvp, const char *add_args[], int count)
    493 {
    494 	char **argv;
    495 	int argc;
    496 
    497 	for (argv = *argvp, argc = 0; (*argvp)[argc]; argc++)
    498 		;
    499 
    500 	argv = malloc((argc + count + 1) * sizeof(char *));
    501 	if (!argv) {
    502 		printf("Out of memory for %d argv\n", count);
    503 		return -ENOMEM;
    504 	}
    505 	memcpy(argv, *argvp, argc * sizeof(char *));
    506 	memcpy(argv + argc, add_args, count * sizeof(char *));
    507 	argv[argc + count] = NULL;
    508 
    509 	*argvp = argv;
    510 	return 0;
    511 }
    512 
    513 int os_jump_to_image(const void *dest, int size)
    514 {
    515 	struct sandbox_state *state = state_get_current();
    516 	char fname[30], mem_fname[30];
    517 	int fd, err;
    518 	const char *extra_args[5];
    519 	char **argv = state->argv;
    520 #ifdef DEBUG
    521 	int argc, i;
    522 #endif
    523 
    524 	err = make_exec(fname, dest, size);
    525 	if (err)
    526 		return err;
    527 
    528 	strcpy(mem_fname, "/tmp/u-boot.mem.XXXXXX");
    529 	fd = mkstemp(mem_fname);
    530 	if (fd < 0)
    531 		return -ENOENT;
    532 	close(fd);
    533 	err = os_write_ram_buf(mem_fname);
    534 	if (err)
    535 		return err;
    536 
    537 	os_fd_restore();
    538 
    539 	extra_args[0] = "-j";
    540 	extra_args[1] = fname;
    541 	extra_args[2] = "-m";
    542 	extra_args[3] = mem_fname;
    543 	extra_args[4] = "--rm_memory";
    544 	err = add_args(&argv, extra_args,
    545 		       sizeof(extra_args) / sizeof(extra_args[0]));
    546 	if (err)
    547 		return err;
    548 
    549 #ifdef DEBUG
    550 	for (i = 0; argv[i]; i++)
    551 		printf("%d %s\n", i, argv[i]);
    552 #endif
    553 
    554 	if (state_uninit())
    555 		os_exit(2);
    556 
    557 	err = execv(fname, argv);
    558 	free(argv);
    559 	if (err)
    560 		return err;
    561 
    562 	return unlink(fname);
    563 }
    564 
    565 int os_find_u_boot(char *fname, int maxlen)
    566 {
    567 	struct sandbox_state *state = state_get_current();
    568 	const char *progname = state->argv[0];
    569 	int len = strlen(progname);
    570 	char *p;
    571 	int fd;
    572 
    573 	if (len >= maxlen || len < 4)
    574 		return -ENOSPC;
    575 
    576 	/* Look for 'u-boot' in the same directory as 'u-boot-spl' */
    577 	strcpy(fname, progname);
    578 	if (!strcmp(fname + len - 4, "-spl")) {
    579 		fname[len - 4] = '\0';
    580 		fd = os_open(fname, O_RDONLY);
    581 		if (fd >= 0) {
    582 			close(fd);
    583 			return 0;
    584 		}
    585 	}
    586 
    587 	/* Look for 'u-boot' in the parent directory of spl/ */
    588 	p = strstr(fname, "/spl/");
    589 	if (p) {
    590 		strcpy(p, p + 4);
    591 		fd = os_open(fname, O_RDONLY);
    592 		if (fd >= 0) {
    593 			close(fd);
    594 			return 0;
    595 		}
    596 	}
    597 
    598 	return -ENOENT;
    599 }
    600 
    601 int os_spl_to_uboot(const char *fname)
    602 {
    603 	struct sandbox_state *state = state_get_current();
    604 	char *argv[state->argc + 1];
    605 	int ret;
    606 
    607 	memcpy(argv, state->argv, sizeof(char *) * (state->argc + 1));
    608 	argv[0] = (char *)fname;
    609 	ret = execv(fname, argv);
    610 	if (ret)
    611 		return ret;
    612 
    613 	return unlink(fname);
    614 }
    615 
    616 void os_localtime(struct rtc_time *rt)
    617 {
    618 	time_t t = time(NULL);
    619 	struct tm *tm;
    620 
    621 	tm = localtime(&t);
    622 	rt->tm_sec = tm->tm_sec;
    623 	rt->tm_min = tm->tm_min;
    624 	rt->tm_hour = tm->tm_hour;
    625 	rt->tm_mday = tm->tm_mday;
    626 	rt->tm_mon = tm->tm_mon + 1;
    627 	rt->tm_year = tm->tm_year + 1900;
    628 	rt->tm_wday = tm->tm_wday;
    629 	rt->tm_yday = tm->tm_yday;
    630 	rt->tm_isdst = tm->tm_isdst;
    631 }
    632 
    633 int os_setjmp(ulong *jmp, int size)
    634 {
    635 	jmp_buf dummy;
    636 
    637 	/*
    638 	 * We cannot rely on the struct name that jmp_buf uses, so use a
    639 	 * local variable here
    640 	 */
    641 	if (size < sizeof(dummy)) {
    642 		printf("setjmp: jmpbuf is too small (%d bytes, need %d)\n",
    643 		       size, sizeof(jmp_buf));
    644 		return -ENOSPC;
    645 	}
    646 
    647 	return setjmp((struct __jmp_buf_tag *)jmp);
    648 }
    649 
    650 void os_longjmp(ulong *jmp, int ret)
    651 {
    652 	longjmp((struct __jmp_buf_tag *)jmp, ret);
    653 }
    654