summaryrefslogtreecommitdiff
path: root/tools/mksunxiboot.c
blob: fa54bceb63b9fda4e1f5d6d970b11640070c6e20 (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
/*
 * (C) Copyright 2007-2011
 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
 * Tom Cubie <tangliang@allwinnertech.com>
 *
 * a simple tool to generate bootable image for sunxi platform.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "../arch/arm/include/asm/arch-sunxi/spl.h"

#define STAMP_VALUE                     0x5F0A6C39

/* check sum functon from sun4i boot code */
int gen_check_sum(struct boot_file_head *head_p)
{
	uint32_t length;
	uint32_t *buf;
	uint32_t loop;
	uint32_t i;
	uint32_t sum;

	length = le32_to_cpu(head_p->length);
	if ((length & 0x3) != 0)	/* must 4-byte-aligned */
		return -1;
	buf = (uint32_t *)head_p;
	head_p->check_sum = cpu_to_le32(STAMP_VALUE);	/* fill stamp */
	loop = length >> 2;

	/* calculate the sum */
	for (i = 0, sum = 0; i < loop; i++)
		sum += le32_to_cpu(buf[i]);

	/* write back check sum */
	head_p->check_sum = cpu_to_le32(sum);

	return 0;
}

#define ALIGN(x, a) __ALIGN_MASK((x), (typeof(x))(a)-1)
#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))

#define SUN4I_SRAM_SIZE		0x7600	/* 0x7748+ is used by BROM */
#define SUNXI_MAX_SRAM_SIZE	0x8000
#define SRAM_LOAD_MAX_SIZE (SUNXI_MAX_SRAM_SIZE - sizeof(struct boot_file_head))

/*
 * BROM (at least on A10 and A20) requires NAND-images to be explicitly aligned
 * to a multiple of 8K, and rejects the image otherwise. MMC-images are fine
 * with 512B blocks.
 */
#define BLOCK_SIZE_NAND 0x2000
#define BLOCK_SIZE_MMC  0x0200
#define MAX_BLOCK_SIZE	BLOCK_SIZE_NAND

struct boot_img {
	struct boot_file_head header;
	char code[SRAM_LOAD_MAX_SIZE];
	char pad[MAX_BLOCK_SIZE];
};

int main(int argc, char *argv[])
{
	int fd_in, fd_out;
	int ac = 1;
	struct boot_img img;
	unsigned file_size;
	unsigned size_limit = SUN4I_SRAM_SIZE - sizeof(struct boot_file_head);
	unsigned block_size = BLOCK_SIZE_NAND;
	int count;

	if (argc < 2) {
		printf("\tThis program makes an input bin file to sun4i " \
		       "bootable image.\n" \
		       "\tUsage: %s input_file [out_putfile]\n", argv[0]);
		return EXIT_FAILURE;
	}

	if (!strcmp(argv[ac], "--mmc")) {
		block_size = BLOCK_SIZE_MMC;
		ac++;
	}
	if (!strcmp(argv[ac], "--nand")) {
		block_size = BLOCK_SIZE_NAND;
		ac++;
	}
	if (!strcmp(argv[ac], "--max")) {
		size_limit = SUNXI_MAX_SRAM_SIZE - sizeof(struct boot_file_head);
		ac++;
	}
	if (ac >= argc)
		return EXIT_FAILURE;

	fd_in = open(argv[ac++], O_RDONLY);
	if (fd_in < 0) {
		perror("Open input file");
		return EXIT_FAILURE;
	}

	memset(&img, 0, sizeof(img));

	/* get input file size */
	file_size = lseek(fd_in, 0, SEEK_END);

	if (file_size > size_limit) {
		fprintf(stderr, "ERROR: File too large!\n");
		return EXIT_FAILURE;
	}

	if (ac >= argc) {
		fd_out = STDOUT_FILENO;
	} else {
		fd_out = open(argv[ac], O_WRONLY | O_CREAT, 0666);
		if (fd_out < 0) {
			perror("Open output file");
			return EXIT_FAILURE;
		}
	}

	/* read file to buffer to calculate checksum */
	lseek(fd_in, 0, SEEK_SET);
	count = read(fd_in, img.code, file_size);
	if (count != file_size) {
		perror("Reading input image");
		return EXIT_FAILURE;
	}

	/* fill the header */
	img.header.b_instruction =	/* b instruction */
		0xEA000000 |	/* jump to the first instr after the header */
		((sizeof(struct boot_file_head) / sizeof(int) - 2)
		 & 0x00FFFFFF);
	memcpy(img.header.magic, BOOT0_MAGIC, 8);	/* no '0' termination */
	img.header.length =
		ALIGN(file_size + sizeof(struct boot_file_head), block_size);
	img.header.b_instruction = cpu_to_le32(img.header.b_instruction);
	img.header.length = cpu_to_le32(img.header.length);

	memcpy(img.header.spl_signature, SPL_SIGNATURE, 3); /* "sunxi" marker */
	img.header.spl_signature[3] = SPL_HEADER_VERSION;

	gen_check_sum(&img.header);

	count = write(fd_out, &img, le32_to_cpu(img.header.length));
	if (count != le32_to_cpu(img.header.length)) {
		perror("Writing output");
		return EXIT_FAILURE;
	}

	close(fd_in);
	close(fd_out);

	return EXIT_SUCCESS;
}