os.c 15.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0+
S
Simon Glass 已提交
2 3 4 5
/*
 * Copyright (c) 2011 The Chromium OS Authors.
 */

6
#include <dirent.h>
S
Simon Glass 已提交
7
#include <errno.h>
S
Simon Glass 已提交
8
#include <fcntl.h>
S
Simon Glass 已提交
9
#include <getopt.h>
10
#include <setjmp.h>
11
#include <stdio.h>
12
#include <stdint.h>
S
Simon Glass 已提交
13
#include <stdlib.h>
14
#include <string.h>
M
Mike Frysinger 已提交
15
#include <termios.h>
M
Matthias Weisser 已提交
16
#include <time.h>
S
Simon Glass 已提交
17
#include <unistd.h>
18
#include <sys/mman.h>
S
Simon Glass 已提交
19
#include <sys/stat.h>
20
#include <sys/time.h>
S
Simon Glass 已提交
21
#include <sys/types.h>
M
Matthias Weisser 已提交
22
#include <linux/types.h>
S
Simon Glass 已提交
23

S
Simon Glass 已提交
24 25 26
#include <asm/getopt.h>
#include <asm/sections.h>
#include <asm/state.h>
S
Simon Glass 已提交
27
#include <os.h>
28
#include <rtc_def.h>
S
Simon Glass 已提交
29 30 31

/* Operating System Interface */

32 33 34 35
struct os_mem_hdr {
	size_t length;		/* number of bytes in the block */
};

S
Simon Glass 已提交
36 37 38 39 40 41 42 43 44 45
ssize_t os_read(int fd, void *buf, size_t count)
{
	return read(fd, buf, count);
}

ssize_t os_write(int fd, const void *buf, size_t count)
{
	return write(fd, buf, count);
}

M
Mike Frysinger 已提交
46 47 48 49 50 51 52 53 54 55 56 57 58
off_t os_lseek(int fd, off_t offset, int whence)
{
	if (whence == OS_SEEK_SET)
		whence = SEEK_SET;
	else if (whence == OS_SEEK_CUR)
		whence = SEEK_CUR;
	else if (whence == OS_SEEK_END)
		whence = SEEK_END;
	else
		os_exit(1);
	return lseek(fd, offset, whence);
}

S
Simon Glass 已提交
59
int os_open(const char *pathname, int os_flags)
S
Simon Glass 已提交
60
{
S
Simon Glass 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
	int flags;

	switch (os_flags & OS_O_MASK) {
	case OS_O_RDONLY:
	default:
		flags = O_RDONLY;
		break;

	case OS_O_WRONLY:
		flags = O_WRONLY;
		break;

	case OS_O_RDWR:
		flags = O_RDWR;
		break;
	}

	if (os_flags & OS_O_CREAT)
		flags |= O_CREAT;
80 81
	if (os_flags & OS_O_TRUNC)
		flags |= O_TRUNC;
S
Simon Glass 已提交
82 83

	return open(pathname, flags, 0777);
S
Simon Glass 已提交
84 85 86 87 88 89 90
}

int os_close(int fd)
{
	return close(fd);
}

91 92 93 94 95
int os_unlink(const char *pathname)
{
	return unlink(pathname);
}

S
Simon Glass 已提交
96 97 98 99
void os_exit(int exit_code)
{
	exit(exit_code);
}
M
Mike Frysinger 已提交
100

101
int os_write_file(const char *fname, const void *buf, int size)
102 103 104 105 106 107 108 109 110 111
{
	int fd;

	fd = os_open(fname, OS_O_WRONLY | OS_O_CREAT | OS_O_TRUNC);
	if (fd < 0) {
		printf("Cannot open file '%s'\n", fname);
		return -EIO;
	}
	if (os_write(fd, buf, size) != size) {
		printf("Cannot write to file '%s'\n", fname);
112
		os_close(fd);
113 114 115 116 117 118 119
		return -EIO;
	}
	os_close(fd);

	return 0;
}

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
int os_read_file(const char *fname, void **bufp, int *sizep)
{
	off_t size;
	int ret = -EIO;
	int fd;

	fd = os_open(fname, OS_O_RDONLY);
	if (fd < 0) {
		printf("Cannot open file '%s'\n", fname);
		goto err;
	}
	size = os_lseek(fd, 0, OS_SEEK_END);
	if (size < 0) {
		printf("Cannot seek to end of file '%s'\n", fname);
		goto err;
	}
	if (os_lseek(fd, 0, OS_SEEK_SET) < 0) {
		printf("Cannot seek to start of file '%s'\n", fname);
		goto err;
	}
140
	*bufp = malloc(size);
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
	if (!*bufp) {
		printf("Not enough memory to read file '%s'\n", fname);
		ret = -ENOMEM;
		goto err;
	}
	if (os_read(fd, *bufp, size) != size) {
		printf("Cannot read from file '%s'\n", fname);
		goto err;
	}
	os_close(fd);
	*sizep = size;

	return 0;
err:
	os_close(fd);
	return ret;
}

M
Mike Frysinger 已提交
159 160
/* Restore tty state when we exit */
static struct termios orig_term;
161
static bool term_setup;
162
static bool term_nonblock;
M
Mike Frysinger 已提交
163

S
Simon Glass 已提交
164
void os_fd_restore(void)
M
Mike Frysinger 已提交
165
{
S
Simon Glass 已提交
166
	if (term_setup) {
167 168
		int flags;

169
		tcsetattr(0, TCSANOW, &orig_term);
170 171 172 173
		if (term_nonblock) {
			flags = fcntl(0, F_GETFL, 0);
			fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
		}
S
Simon Glass 已提交
174 175
		term_setup = false;
	}
M
Mike Frysinger 已提交
176 177 178
}

/* Put tty into raw mode so <tab> and <ctrl+c> work */
179
void os_tty_raw(int fd, bool allow_sigs)
M
Mike Frysinger 已提交
180 181
{
	struct termios term;
182
	int flags;
M
Mike Frysinger 已提交
183

184
	if (term_setup)
M
Mike Frysinger 已提交
185 186 187 188 189 190 191 192 193 194
		return;

	/* If not a tty, don't complain */
	if (tcgetattr(fd, &orig_term))
		return;

	term = orig_term;
	term.c_iflag = IGNBRK | IGNPAR;
	term.c_oflag = OPOST | ONLCR;
	term.c_cflag = CS8 | CREAD | CLOCAL;
195
	term.c_lflag = allow_sigs ? ISIG : 0;
M
Mike Frysinger 已提交
196 197 198
	if (tcsetattr(fd, TCSANOW, &term))
		return;

199 200 201 202 203 204 205
	flags = fcntl(fd, F_GETFL, 0);
	if (!(flags & O_NONBLOCK)) {
		if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
			return;
		term_nonblock = true;
	}

S
Simon Glass 已提交
206
	term_setup = true;
M
Mike Frysinger 已提交
207 208
	atexit(os_fd_restore);
}
209 210 211

void *os_malloc(size_t length)
{
212
	int page_size = getpagesize();
213
	struct os_mem_hdr *hdr;
214

215 216 217 218 219 220
	/*
	 * Use an address that is hopefully available to us so that pointers
	 * to this memory are fairly obvious. If we end up with a different
	 * address, that's fine too.
	 */
	hdr = mmap((void *)0x10000000, length + page_size,
221 222
		   PROT_READ | PROT_WRITE | PROT_EXEC,
		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
223 224 225 226
	if (hdr == MAP_FAILED)
		return NULL;
	hdr->length = length;

227
	return (void *)hdr + page_size;
228 229
}

230
void os_free(void *ptr)
231
{
232 233
	int page_size = getpagesize();
	struct os_mem_hdr *hdr;
234

235 236 237 238
	if (ptr) {
		hdr = ptr - page_size;
		munmap(hdr, hdr->length + page_size);
	}
239 240
}

M
Matthias Weisser 已提交
241 242 243 244 245
void os_usleep(unsigned long usec)
{
	usleep(usec);
}

246
uint64_t __attribute__((no_instrument_function)) os_get_nsec(void)
M
Matthias Weisser 已提交
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
{
#if defined(CLOCK_MONOTONIC) && defined(_POSIX_MONOTONIC_CLOCK)
	struct timespec tp;
	if (EINVAL == clock_gettime(CLOCK_MONOTONIC, &tp)) {
		struct timeval tv;

		gettimeofday(&tv, NULL);
		tp.tv_sec = tv.tv_sec;
		tp.tv_nsec = tv.tv_usec * 1000;
	}
	return tp.tv_sec * 1000000000ULL + tp.tv_nsec;
#else
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
#endif
}
S
Simon Glass 已提交
264 265 266 267 268 269

static char *short_opts;
static struct option *long_opts;

int os_parse_args(struct sandbox_state *state, int argc, char *argv[])
{
270
	struct sandbox_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
S
Simon Glass 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	size_t num_options = __u_boot_sandbox_option_count();
	size_t i;

	int hidden_short_opt;
	size_t si;

	int c;

	if (short_opts || long_opts)
		return 1;

	state->argc = argc;
	state->argv = argv;

	/* dynamically construct the arguments to the system getopt_long */
286 287
	short_opts = malloc(sizeof(*short_opts) * num_options * 2 + 1);
	long_opts = malloc(sizeof(*long_opts) * num_options);
S
Simon Glass 已提交
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
	if (!short_opts || !long_opts)
		return 1;

	/*
	 * getopt_long requires "val" to be unique (since that is what the
	 * func returns), so generate unique values automatically for flags
	 * that don't have a short option.  pick 0x100 as that is above the
	 * single byte range (where ASCII/ISO-XXXX-X charsets live).
	 */
	hidden_short_opt = 0x100;
	si = 0;
	for (i = 0; i < num_options; ++i) {
		long_opts[i].name = sb_opt[i]->flag;
		long_opts[i].has_arg = sb_opt[i]->has_arg ?
			required_argument : no_argument;
		long_opts[i].flag = NULL;

		if (sb_opt[i]->flag_short) {
			short_opts[si++] = long_opts[i].val = sb_opt[i]->flag_short;
			if (long_opts[i].has_arg == required_argument)
				short_opts[si++] = ':';
		} else
			long_opts[i].val = sb_opt[i]->flag_short = hidden_short_opt++;
	}
	short_opts[si] = '\0';

	/* we need to handle output ourselves since u-boot provides printf */
	opterr = 0;

	/*
	 * walk all of the options the user gave us on the command line,
	 * figure out what u-boot option structure they belong to (via
	 * the unique short val key), and call the appropriate callback.
	 */
	while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
		for (i = 0; i < num_options; ++i) {
			if (sb_opt[i]->flag_short == c) {
				if (sb_opt[i]->callback(state, optarg)) {
					state->parse_err = sb_opt[i]->flag;
					return 0;
				}
				break;
			}
		}
		if (i == num_options) {
			/*
			 * store the faulting flag for later display.  we have to
			 * store the flag itself as the getopt parsing itself is
			 * tricky: need to handle the following flags (assume all
			 * of the below are unknown):
			 *   -a        optopt='a' optind=<next>
			 *   -abbbb    optopt='a' optind=<this>
			 *   -aaaaa    optopt='a' optind=<this>
			 *   --a       optopt=0   optind=<this>
			 * as you can see, it is impossible to determine the exact
			 * faulting flag without doing the parsing ourselves, so
			 * we just report the specific flag that failed.
			 */
			if (optopt) {
				static char parse_err[3] = { '-', 0, '\0', };
				parse_err[1] = optopt;
				state->parse_err = parse_err;
			} else
				state->parse_err = argv[optind - 1];
			break;
		}
	}

	return 0;
}
358 359 360 361 362 363 364

void os_dirent_free(struct os_dirent_node *node)
{
	struct os_dirent_node *next;

	while (node) {
		next = node->next;
365
		free(node);
366 367 368 369 370 371
		node = next;
	}
}

int os_dirent_ls(const char *dirname, struct os_dirent_node **headp)
{
372
	struct dirent *entry;
373 374 375 376 377
	struct os_dirent_node *head, *node, *next;
	struct stat buf;
	DIR *dir;
	int ret;
	char *fname;
378
	char *old_fname;
379
	int len;
380
	int dirlen;
381 382 383 384 385 386

	*headp = NULL;
	dir = opendir(dirname);
	if (!dir)
		return -1;

387 388 389
	/* Create a buffer upfront, with typically sufficient size */
	dirlen = strlen(dirname) + 2;
	len = dirlen + 256;
390
	fname = malloc(len);
391 392 393 394 395 396
	if (!fname) {
		ret = -ENOMEM;
		goto done;
	}

	for (node = head = NULL;; node = next) {
397 398 399 400
		errno = 0;
		entry = readdir(dir);
		if (!entry) {
			ret = errno;
401
			break;
402
		}
403
		next = malloc(sizeof(*node) + strlen(entry->d_name) + 1);
404
		if (!next) {
405 406 407 408
			os_dirent_free(head);
			ret = -ENOMEM;
			goto done;
		}
409 410 411
		if (dirlen + strlen(entry->d_name) > len) {
			len = dirlen + strlen(entry->d_name);
			old_fname = fname;
412
			fname = realloc(fname, len);
413
			if (!fname) {
414 415
				free(old_fname);
				free(next);
416 417 418 419 420
				os_dirent_free(head);
				ret = -ENOMEM;
				goto done;
			}
		}
421
		next->next = NULL;
422 423
		strcpy(next->name, entry->d_name);
		switch (entry->d_type) {
424 425 426 427 428 429 430 431 432
		case DT_REG:
			next->type = OS_FILET_REG;
			break;
		case DT_DIR:
			next->type = OS_FILET_DIR;
			break;
		case DT_LNK:
			next->type = OS_FILET_LNK;
			break;
433 434
		default:
			next->type = OS_FILET_UNKNOWN;
435 436 437 438 439 440 441
		}
		next->size = 0;
		snprintf(fname, len, "%s/%s", dirname, next->name);
		if (!stat(fname, &buf))
			next->size = buf.st_size;
		if (node)
			node->next = next;
442 443
		else
			head = next;
444 445 446 447 448
	}
	*headp = head;

done:
	closedir(dir);
449
	free(fname);
450 451 452 453 454 455 456 457 458 459 460 461
	return ret;
}

const char *os_dirent_typename[OS_FILET_COUNT] = {
	"   ",
	"SYM",
	"DIR",
	"???",
};

const char *os_dirent_get_typename(enum os_dirent_t type)
{
462
	if (type >= OS_FILET_REG && type < OS_FILET_COUNT)
463 464 465 466 467
		return os_dirent_typename[type];

	return os_dirent_typename[OS_FILET_UNKNOWN];
}

468
int os_get_filesize(const char *fname, loff_t *size)
469 470 471 472 473 474 475
{
	struct stat buf;
	int ret;

	ret = stat(fname, &buf);
	if (ret)
		return ret;
476 477
	*size = buf.st_size;
	return 0;
478
}
479

480 481 482 483 484 485 486 487 488 489 490
void os_putc(int ch)
{
	putchar(ch);
}

void os_puts(const char *str)
{
	while (*str)
		os_putc(*str++);
}

491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
int os_write_ram_buf(const char *fname)
{
	struct sandbox_state *state = state_get_current();
	int fd, ret;

	fd = open(fname, O_CREAT | O_WRONLY, 0777);
	if (fd < 0)
		return -ENOENT;
	ret = write(fd, state->ram_buf, state->ram_size);
	close(fd);
	if (ret != state->ram_size)
		return -EIO;

	return 0;
}

int os_read_ram_buf(const char *fname)
{
	struct sandbox_state *state = state_get_current();
	int fd, ret;
511
	loff_t size;
512

513 514 515
	ret = os_get_filesize(fname, &size);
	if (ret < 0)
		return ret;
516 517 518 519 520 521 522 523 524 525 526 527 528
	if (size != state->ram_size)
		return -ENOSPC;
	fd = open(fname, O_RDONLY);
	if (fd < 0)
		return -ENOENT;

	ret = read(fd, state->ram_buf, state->ram_size);
	close(fd);
	if (ret != state->ram_size)
		return -EIO;

	return 0;
}
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546

static int make_exec(char *fname, const void *data, int size)
{
	int fd;

	strcpy(fname, "/tmp/u-boot.jump.XXXXXX");
	fd = mkstemp(fname);
	if (fd < 0)
		return -ENOENT;
	if (write(fd, data, size) < 0)
		return -EIO;
	close(fd);
	if (chmod(fname, 0777))
		return -ENOEXEC;

	return 0;
}

547 548 549 550 551 552 553 554 555 556 557 558
/**
 * add_args() - Allocate a new argv with the given args
 *
 * This is used to create a new argv array with all the old arguments and some
 * new ones that are passed in
 *
 * @argvp:  Returns newly allocated args list
 * @add_args: Arguments to add, each a string
 * @count: Number of arguments in @add_args
 * @return 0 if OK, -ENOMEM if out of memory
 */
static int add_args(char ***argvp, char *add_args[], int count)
559
{
560
	char **argv, **ap;
561 562
	int argc;

563
	for (argc = 0; (*argvp)[argc]; argc++)
564 565
		;

566
	argv = malloc((argc + count + 1) * sizeof(char *));
567 568 569 570
	if (!argv) {
		printf("Out of memory for %d argv\n", count);
		return -ENOMEM;
	}
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
	for (ap = *argvp, argc = 0; *ap; ap++) {
		char *arg = *ap;

		/* Drop args that we don't want to propagate */
		if (*arg == '-' && strlen(arg) == 2) {
			switch (arg[1]) {
			case 'j':
			case 'm':
				ap++;
				continue;
			}
		} else if (!strcmp(arg, "--rm_memory")) {
			ap++;
			continue;
		}
		argv[argc++] = arg;
	}

589 590 591 592 593 594 595
	memcpy(argv + argc, add_args, count * sizeof(char *));
	argv[argc + count] = NULL;

	*argvp = argv;
	return 0;
}

596 597 598 599 600 601 602 603 604 605
/**
 * os_jump_to_file() - Jump to a new program
 *
 * This saves the memory buffer, sets up arguments to the new process, then
 * execs it.
 *
 * @fname: Filename to exec
 * @return does not return on success, any return value is an error
 */
static int os_jump_to_file(const char *fname)
606 607
{
	struct sandbox_state *state = state_get_current();
608
	char mem_fname[30];
609
	int fd, err;
610
	char *extra_args[5];
611
	char **argv = state->argv;
612
	int argc;
613
#ifdef DEBUG
614
	int i;
615 616 617 618 619 620 621 622 623 624 625 626 627 628
#endif

	strcpy(mem_fname, "/tmp/u-boot.mem.XXXXXX");
	fd = mkstemp(mem_fname);
	if (fd < 0)
		return -ENOENT;
	close(fd);
	err = os_write_ram_buf(mem_fname);
	if (err)
		return err;

	os_fd_restore();

	extra_args[0] = "-j";
629
	extra_args[1] = (char *)fname;
630 631
	extra_args[2] = "-m";
	extra_args[3] = mem_fname;
632 633 634 635
	argc = 4;
	if (state->ram_buf_rm)
		extra_args[argc++] = "--rm_memory";
	err = add_args(&argv, extra_args, argc);
636 637
	if (err)
		return err;
638
	argv[0] = (char *)fname;
639 640 641 642 643 644 645 646 647 648

#ifdef DEBUG
	for (i = 0; argv[i]; i++)
		printf("%d %s\n", i, argv[i]);
#endif

	if (state_uninit())
		os_exit(2);

	err = execv(fname, argv);
649
	free(argv);
650 651
	if (err) {
		perror("Unable to run image");
652
		printf("Image filename '%s'\n", fname);
653
		return err;
654
	}
655 656 657

	return unlink(fname);
}
658

659 660 661 662 663 664 665 666 667 668 669 670
int os_jump_to_image(const void *dest, int size)
{
	char fname[30];
	int err;

	err = make_exec(fname, dest, size);
	if (err)
		return err;

	return os_jump_to_file(fname);
}

671 672 673 674 675
int os_find_u_boot(char *fname, int maxlen)
{
	struct sandbox_state *state = state_get_current();
	const char *progname = state->argv[0];
	int len = strlen(progname);
676
	const char *suffix;
677 678 679 680 681 682 683
	char *p;
	int fd;

	if (len >= maxlen || len < 4)
		return -ENOSPC;

	strcpy(fname, progname);
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
	suffix = fname + len - 4;

	/* If we are TPL, boot to SPL */
	if (!strcmp(suffix, "-tpl")) {
		fname[len - 3] = 's';
		fd = os_open(fname, O_RDONLY);
		if (fd >= 0) {
			close(fd);
			return 0;
		}

		/* Look for 'u-boot-tpl' in the tpl/ directory */
		p = strstr(fname, "/tpl/");
		if (p) {
			p[1] = 's';
			fd = os_open(fname, O_RDONLY);
			if (fd >= 0) {
				close(fd);
				return 0;
			}
		}
		return -ENOENT;
	}

	/* Look for 'u-boot' in the same directory as 'u-boot-spl' */
	if (!strcmp(suffix, "-spl")) {
710 711 712 713 714 715 716 717 718
		fname[len - 4] = '\0';
		fd = os_open(fname, O_RDONLY);
		if (fd >= 0) {
			close(fd);
			return 0;
		}
	}

	/* Look for 'u-boot' in the parent directory of spl/ */
719
	p = strstr(fname, "spl/");
720
	if (p) {
721 722
		/* Remove the "spl" characters */
		memmove(p, p + 4, strlen(p + 4) + 1);
723 724 725 726 727 728 729 730 731 732 733 734
		fd = os_open(fname, O_RDONLY);
		if (fd >= 0) {
			close(fd);
			return 0;
		}
	}

	return -ENOENT;
}

int os_spl_to_uboot(const char *fname)
{
735
	return os_jump_to_file(fname);
736 737
}

738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
void os_localtime(struct rtc_time *rt)
{
	time_t t = time(NULL);
	struct tm *tm;

	tm = localtime(&t);
	rt->tm_sec = tm->tm_sec;
	rt->tm_min = tm->tm_min;
	rt->tm_hour = tm->tm_hour;
	rt->tm_mday = tm->tm_mday;
	rt->tm_mon = tm->tm_mon + 1;
	rt->tm_year = tm->tm_year + 1900;
	rt->tm_wday = tm->tm_wday;
	rt->tm_yday = tm->tm_yday;
	rt->tm_isdst = tm->tm_isdst;
}
754

755 756 757 758
void os_abort(void)
{
	abort();
}
759 760 761 762 763 764 765 766 767 768 769

int os_mprotect_allow(void *start, size_t len)
{
	int page_size = getpagesize();

	/* Move start to the start of a page, len to the end */
	start = (void *)(((ulong)start) & ~(page_size - 1));
	len = (len + page_size * 2) & ~(page_size - 1);

	return mprotect(start, len, PROT_READ | PROT_WRITE);
}
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795

void *os_find_text_base(void)
{
	char line[500];
	void *base = NULL;
	int len;
	int fd;

	/*
	 * This code assumes that the first line of /proc/self/maps holds
	 * information about the text, for example:
	 *
	 * 5622d9907000-5622d9a55000 r-xp 00000000 08:01 15067168   u-boot
	 *
	 * The first hex value is assumed to be the address.
	 *
	 * This is tested in Linux 4.15.
	 */
	fd = open("/proc/self/maps", O_RDONLY);
	if (fd == -1)
		return NULL;
	len = read(fd, line, sizeof(line));
	if (len > 0) {
		char *end = memchr(line, '-', len);

		if (end) {
796
			uintptr_t addr;
797 798

			*end = '\0';
799
			if (sscanf(line, "%zx", &addr) == 1)
800 801 802 803 804 805 806
				base = (void *)addr;
		}
	}
	close(fd);

	return base;
}