| /* | 
 |  * vrl4 format generator | 
 |  * | 
 |  * Copyright (C) 2010 Simon Horman | 
 |  * | 
 |  * This file is subject to the terms and conditions of the GNU General Public | 
 |  * License.  See the file "COPYING" in the main directory of this archive | 
 |  * for more details. | 
 |  */ | 
 |  | 
 | /* | 
 |  * usage: vrl4 < zImage > out | 
 |  *	  dd if=out of=/dev/sdx bs=512 seek=1 # Write the image to sector 1 | 
 |  * | 
 |  * Reads a zImage from stdin and writes a vrl4 image to stdout. | 
 |  * In practice this means writing a padded vrl4 header to stdout followed | 
 |  * by the zImage. | 
 |  * | 
 |  * The padding places the zImage at ALIGN bytes into the output. | 
 |  * The vrl4 uses ALIGN + START_BASE as the start_address. | 
 |  * This is where the mask ROM will jump to after verifying the header. | 
 |  * | 
 |  * The header sets copy_size to min(sizeof(zImage), MAX_BOOT_PROG_LEN) + ALIGN. | 
 |  * That is, the mask ROM will load the padded header (ALIGN bytes) | 
 |  * And then MAX_BOOT_PROG_LEN bytes of the image, or the entire image, | 
 |  * whichever is smaller. | 
 |  * | 
 |  * The zImage is not modified in any way. | 
 |  */ | 
 |  | 
 | #define _BSD_SOURCE | 
 | #include <endian.h> | 
 | #include <unistd.h> | 
 | #include <stdint.h> | 
 | #include <stdio.h> | 
 | #include <errno.h> | 
 |  | 
 | struct hdr { | 
 | 	uint32_t magic1; | 
 | 	uint32_t reserved1; | 
 | 	uint32_t magic2; | 
 | 	uint32_t reserved2; | 
 | 	uint16_t copy_size; | 
 | 	uint16_t boot_options; | 
 | 	uint32_t reserved3; | 
 | 	uint32_t start_address; | 
 | 	uint32_t reserved4; | 
 | 	uint32_t reserved5; | 
 | 	char     reserved6[308]; | 
 | }; | 
 |  | 
 | #define DECLARE_HDR(h)					\ | 
 | 	struct hdr (h) = {				\ | 
 | 		.magic1 =	htole32(0xea000000),	\ | 
 | 		.reserved1 =	htole32(0x56),		\ | 
 | 		.magic2 =	htole32(0xe59ff008),	\ | 
 | 		.reserved3 =	htole16(0x1) } | 
 |  | 
 | /* Align to 512 bytes, the MMCIF sector size */ | 
 | #define ALIGN_BITS	9 | 
 | #define ALIGN		(1 << ALIGN_BITS) | 
 |  | 
 | #define START_BASE	0xe55b0000 | 
 |  | 
 | /* | 
 |  * With an alignment of 512 the header uses the first sector. | 
 |  * There is a 128 sector (64kbyte) limit on the data loaded by the mask ROM. | 
 |  * So there are 127 sectors left for the boot programme. But in practice | 
 |  * Only a small portion of a zImage is needed, 16 sectors should be more | 
 |  * than enough. | 
 |  * | 
 |  * Note that this sets how much of the zImage is copied by the mask ROM. | 
 |  * The entire zImage is present after the header and is loaded | 
 |  * by the code in the boot program (which is the first portion of the zImage). | 
 |  */ | 
 | #define	MAX_BOOT_PROG_LEN (16 * 512) | 
 |  | 
 | #define ROUND_UP(x)	((x + ALIGN - 1) & ~(ALIGN - 1)) | 
 |  | 
 | ssize_t do_read(int fd, void *buf, size_t count) | 
 | { | 
 | 	size_t offset = 0; | 
 | 	ssize_t l; | 
 |  | 
 | 	while (offset < count) { | 
 | 		l = read(fd, buf + offset, count - offset); | 
 | 		if (!l) | 
 | 			break; | 
 | 		if (l < 0) { | 
 | 			if (errno == EAGAIN || errno == EWOULDBLOCK) | 
 | 				continue; | 
 | 			perror("read"); | 
 | 			return -1; | 
 | 		} | 
 | 		offset += l; | 
 | 	} | 
 |  | 
 | 	return offset; | 
 | } | 
 |  | 
 | ssize_t do_write(int fd, const void *buf, size_t count) | 
 | { | 
 | 	size_t offset = 0; | 
 | 	ssize_t l; | 
 |  | 
 | 	while (offset < count) { | 
 | 		l = write(fd, buf + offset, count - offset); | 
 | 		if (l < 0) { | 
 | 			if (errno == EAGAIN || errno == EWOULDBLOCK) | 
 | 				continue; | 
 | 			perror("write"); | 
 | 			return -1; | 
 | 		} | 
 | 		offset += l; | 
 | 	} | 
 |  | 
 | 	return offset; | 
 | } | 
 |  | 
 | ssize_t write_zero(int fd, size_t len) | 
 | { | 
 | 	size_t i = len; | 
 |  | 
 | 	while (i--) { | 
 | 		const char x = 0; | 
 | 		if (do_write(fd, &x, 1) < 0) | 
 | 			return -1; | 
 | 	} | 
 |  | 
 | 	return len; | 
 | } | 
 |  | 
 | int main(void) | 
 | { | 
 | 	DECLARE_HDR(hdr); | 
 | 	char boot_program[MAX_BOOT_PROG_LEN]; | 
 | 	size_t aligned_hdr_len, alligned_prog_len; | 
 | 	ssize_t prog_len; | 
 |  | 
 | 	prog_len = do_read(0, boot_program, sizeof(boot_program)); | 
 | 	if (prog_len <= 0) | 
 | 		return -1; | 
 |  | 
 | 	aligned_hdr_len = ROUND_UP(sizeof(hdr)); | 
 | 	hdr.start_address = htole32(START_BASE + aligned_hdr_len); | 
 | 	alligned_prog_len = ROUND_UP(prog_len); | 
 | 	hdr.copy_size = htole16(aligned_hdr_len + alligned_prog_len); | 
 |  | 
 | 	if (do_write(1, &hdr, sizeof(hdr)) < 0) | 
 | 		return -1; | 
 | 	if (write_zero(1, aligned_hdr_len - sizeof(hdr)) < 0) | 
 | 		return -1; | 
 |  | 
 | 	if (do_write(1, boot_program, prog_len) < 0) | 
 | 		return 1; | 
 |  | 
 | 	/* Write out the rest of the kernel */ | 
 | 	while (1) { | 
 | 		prog_len = do_read(0, boot_program, sizeof(boot_program)); | 
 | 		if (prog_len < 0) | 
 | 			return 1; | 
 | 		if (prog_len == 0) | 
 | 			break; | 
 | 		if (do_write(1, boot_program, prog_len) < 0) | 
 | 			return 1; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } |