aboutsummaryrefslogtreecommitdiff
path: root/tools/sfspl.c
blob: c76420ce21b63bf1c66b78eb068fc70effe1c2b5 (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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
 *
 * The StarFive JH7110 requires to prepend a header to u-boot-spl.bin describing
 * the payload length and CRC32.
 *
 * This module implements support in mkimage and dumpimage for this file format.
 *
 * StarFive's spl_tool available under GPL-2.0-and-later at
 * https://github.com/starfive-tech/Tools implements writing the same file
 * format and served as a reference.
 */

#include <compiler.h>
#include <fcntl.h>
#include <u-boot/crc.h>
#include <unistd.h>
#include "imagetool.h"

#define DEFAULT_VERSION 0x01010101
#define DEFAULT_BACKUP 0x200000U
#define DEFAULT_OFFSET 0x240

/**
 * struct spl_hdr - header for SPL on JH7110
 *
 * All fields are low-endian.
 */
struct spl_hdr {
	/** @offset:	offset to SPL header (0x240) */
	unsigned int offset;
	/** @bkp_offs:	address of backup SPL, defaults to DEFAULT_BACKUP */
	unsigned int bkp_offs;
	/** @zero1:	set to zero */
	unsigned int zero1[159];
	/** @version:	header version, defaults to DEFAULT_VERSION */
	unsigned int version;
	/** @file_size:	file size */
	unsigned int file_size;
	/** @hdr_size:	size of the file header (0x400) */
	unsigned int hdr_size;
	/** @crc32:	CRC32 */
	unsigned int crc32;
	/** @zero2:	set to zero */
	unsigned int zero2[91];
};

static int sfspl_check_params(struct image_tool_params *params)
{
	/* Only the RISC-V architecture is supported */
	if (params->Aflag && params->arch != IH_ARCH_RISCV)
		return EXIT_FAILURE;

	return EXIT_SUCCESS;
}

static int sfspl_verify_header(unsigned char *buf, int size,
			       struct image_tool_params *params)
{
	struct spl_hdr *hdr = (void *)buf;
	unsigned int hdr_size = le32_to_cpu(hdr->hdr_size);
	unsigned int file_size = le32_to_cpu(hdr->file_size);
	unsigned int crc = le32_to_cpu(hdr->crc32);
	unsigned int crc_check;

	if (size < 0 ||
	    (size_t)size < sizeof(struct spl_hdr) ||
	    (size_t)size < hdr_size + file_size) {
		printf("Truncated file\n");
		return EXIT_FAILURE;
	}
	if (hdr->version != DEFAULT_VERSION) {
		printf("Unknown file format version\n");
		return EXIT_FAILURE;
	}
	crc_check = crc32(0, &buf[hdr_size], size - hdr_size);
	if (crc_check != crc) {
		printf("Incorrect CRC32\n");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

static void sfspl_print_header(const void *buf,
			       struct image_tool_params *params)
{
	struct spl_hdr *hdr = (void *)buf;
	unsigned int hdr_size = le32_to_cpu(hdr->hdr_size);
	unsigned int file_size = le32_to_cpu(hdr->file_size);

	printf("Header size: %u\n", hdr_size);
	printf("Payload size: %u\n", file_size);
}

static int sfspl_image_extract_subimage(void *ptr,
					struct image_tool_params *params)
{
	struct spl_hdr *hdr = (void *)ptr;
	unsigned char *buf = ptr;
	int fd, ret = EXIT_SUCCESS;
	unsigned int hdr_size = le32_to_cpu(hdr->hdr_size);
	unsigned int file_size = le32_to_cpu(hdr->file_size);

	if (params->pflag) {
		printf("Invalid image index %d\n", params->pflag);
		return EXIT_FAILURE;
	}

	fd = open(params->outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (fd == -1) {
		perror("Cannot open file");
		return EXIT_FAILURE;
	}
	if (write(fd, &buf[hdr_size], file_size) != file_size) {
		perror("Cannot write file");
		ret = EXIT_FAILURE;
	}
	close(fd);

	return ret;
}

static int sfspl_check_image_type(uint8_t type)
{
	if (type == IH_TYPE_STARFIVE_SPL)
		return EXIT_SUCCESS;

	return EXIT_FAILURE;
}

static void sfspl_set_header(void *buf, struct stat *sbuf, int infd,
			     struct image_tool_params *params)
{
	struct spl_hdr *hdr = buf;
	unsigned int file_size;
	unsigned int crc;

	file_size = params->file_size - sizeof(struct spl_hdr);
	crc = crc32(0, &((unsigned char *)buf)[sizeof(struct spl_hdr)],
		    file_size);

	hdr->offset = cpu_to_le32(DEFAULT_OFFSET);
	hdr->bkp_offs = cpu_to_le32(DEFAULT_BACKUP);
	hdr->version = cpu_to_le32(DEFAULT_VERSION);
	hdr->file_size = cpu_to_le32(file_size);
	hdr->hdr_size = cpu_to_le32(sizeof(struct spl_hdr));
	hdr->crc32 = cpu_to_le32(crc);
}

static int sfspl_vrec_header(struct image_tool_params *params,
			     struct image_type_params *tparams)
{
	tparams->hdr = calloc(sizeof(struct spl_hdr), 1);

	/* No padding */
	return 0;
}

U_BOOT_IMAGE_TYPE(
	sfspl, /* id */
	"StarFive SPL Image", /* name */
	sizeof(struct spl_hdr), /* header_size */
	NULL, /* header */
	sfspl_check_params, /* check_params */
	sfspl_verify_header, /* verify header */
	sfspl_print_header, /* print header */
	sfspl_set_header, /* set header */
	sfspl_image_extract_subimage, /* extract_subimage */
	sfspl_check_image_type, /* check_image_type */
	NULL, /* fflag_handle */
	sfspl_vrec_header /* vrec_header */
);