summaryrefslogtreecommitdiff
path: root/common/load.c
blob: 4669c0bc073f85c06af455d0b55841a97d22a29e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * load.c - pull the payload into RAM. semihosting is the qemu path,
 * flash and block devices follow the same shape once they exist.
 *
 * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
 */

#include <string.h>
#include <sys/types.h>
#include <stdint.h>
#include <semihosting.h>
#include <boot.h>

/*
 * load fname into mem_base. the header decides the real placement, so
 * we read the whole file first and let tb_image_setup place it.
 */
int tb_load_semihosting(const char *fname, uintptr_t load_addr,
			uintptr_t mem_base, uint32 mem_size,
			struct tb_image *img)
{
	struct Image_header *ih = (struct Image_header *)load_addr;
	long fd;
	long len;
	long ret;

	fd = smh_open(fname, MODE_READ | MODE_BINARY);
	if (fd < 0)
		return fd;

	len = smh_flen(fd);
	if (len < 0) {
		smh_close(fd);
		return len;
	}

	if ((uint32)len > mem_size) {
		smh_close(fd);
		return -2;
	}

	/* header first so placement is known before the big copy */
	ret = smh_read(fd, (void *)load_addr, sizeof(*ih));
	if (ret != sizeof(*ih)) {
		smh_close(fd);
		return -3;
	}

	if (tb_image_setup(load_addr, mem_base, mem_size, img)) {
		smh_close(fd);
		return -4;
	}

	if (img->load != load_addr) {
		/* the image wants to sit elsewhere, copy it there */
		memmove((void *)img->load, (void *)load_addr,
			sizeof(*ih));
	}

	ret = smh_read(fd, (void *)(img->load + sizeof(*ih)),
		       len - sizeof(*ih));
	if (ret != len - sizeof(*ih)) {
		smh_close(fd);
		return -5;
	}

	smh_close(fd);
	return 0;
}