237 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * Copyright 2014 Broadcom Corporation
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier:	GPL-2.0+
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * Minimal semihosting implementation for reading files into memory. If more
 | 
						|
 * features like writing files or console output are required they can be
 | 
						|
 * added later. This code has been tested on arm64/aarch64 fastmodel only.
 | 
						|
 * An untested placeholder exists for armv7 architectures, but since they
 | 
						|
 * are commonly available in silicon now, fastmodel usage makes less sense
 | 
						|
 * for them.
 | 
						|
 */
 | 
						|
#include <common.h>
 | 
						|
#include <asm/semihosting.h>
 | 
						|
 | 
						|
#define SYSOPEN		0x01
 | 
						|
#define SYSCLOSE	0x02
 | 
						|
#define SYSREAD		0x06
 | 
						|
#define SYSFLEN		0x0C
 | 
						|
 | 
						|
#define MODE_READ	0x0
 | 
						|
#define MODE_READBIN	0x1
 | 
						|
 | 
						|
/*
 | 
						|
 * Call the handler
 | 
						|
 */
 | 
						|
static long smh_trap(unsigned int sysnum, void *addr)
 | 
						|
{
 | 
						|
	register long result asm("r0");
 | 
						|
#if defined(CONFIG_ARM64)
 | 
						|
	asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
 | 
						|
#else
 | 
						|
	/* Note - untested placeholder */
 | 
						|
	asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
 | 
						|
#endif
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
 | 
						|
 * descriptor or -1 on error.
 | 
						|
 */
 | 
						|
static long smh_open(const char *fname, char *modestr)
 | 
						|
{
 | 
						|
	long fd;
 | 
						|
	unsigned long mode;
 | 
						|
	struct smh_open_s {
 | 
						|
		const char *fname;
 | 
						|
		unsigned long mode;
 | 
						|
		size_t len;
 | 
						|
	} open;
 | 
						|
 | 
						|
	debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
 | 
						|
 | 
						|
	/* Check the file mode */
 | 
						|
	if (!(strcmp(modestr, "r"))) {
 | 
						|
		mode = MODE_READ;
 | 
						|
	} else if (!(strcmp(modestr, "rb"))) {
 | 
						|
		mode = MODE_READBIN;
 | 
						|
	} else {
 | 
						|
		printf("%s: ERROR mode \'%s\' not supported\n", __func__,
 | 
						|
		       modestr);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	open.fname = fname;
 | 
						|
	open.len = strlen(fname);
 | 
						|
	open.mode = mode;
 | 
						|
 | 
						|
	/* Open the file on the host */
 | 
						|
	fd = smh_trap(SYSOPEN, &open);
 | 
						|
	if (fd == -1)
 | 
						|
		printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
 | 
						|
		       fname);
 | 
						|
 | 
						|
	return fd;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
 | 
						|
 */
 | 
						|
static long smh_read(long fd, void *memp, size_t len)
 | 
						|
{
 | 
						|
	long ret;
 | 
						|
	struct smh_read_s {
 | 
						|
		long fd;
 | 
						|
		void *memp;
 | 
						|
		size_t len;
 | 
						|
	} read;
 | 
						|
 | 
						|
	debug("%s: fd %ld, memp %p, len %lu\n", __func__, fd, memp, len);
 | 
						|
 | 
						|
	read.fd = fd;
 | 
						|
	read.memp = memp;
 | 
						|
	read.len = len;
 | 
						|
 | 
						|
	ret = smh_trap(SYSREAD, &read);
 | 
						|
	if (ret < 0) {
 | 
						|
		/*
 | 
						|
		 * The ARM handler allows for returning partial lengths,
 | 
						|
		 * but in practice this never happens so rather than create
 | 
						|
		 * hard to maintain partial read loops and such, just fail
 | 
						|
		 * with an error message.
 | 
						|
		 */
 | 
						|
		printf("%s: ERROR ret %ld, fd %ld, len %lu memp %p\n",
 | 
						|
		       __func__, ret, fd, len, memp);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Close the file using the file descriptor
 | 
						|
 */
 | 
						|
static long smh_close(long fd)
 | 
						|
{
 | 
						|
	long ret;
 | 
						|
 | 
						|
	debug("%s: fd %ld\n", __func__, fd);
 | 
						|
 | 
						|
	ret = smh_trap(SYSCLOSE, &fd);
 | 
						|
	if (ret == -1)
 | 
						|
		printf("%s: ERROR fd %ld\n", __func__, fd);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Get the file length from the file descriptor
 | 
						|
 */
 | 
						|
static long smh_len_fd(long fd)
 | 
						|
{
 | 
						|
	long ret;
 | 
						|
 | 
						|
	debug("%s: fd %ld\n", __func__, fd);
 | 
						|
 | 
						|
	ret = smh_trap(SYSFLEN, &fd);
 | 
						|
	if (ret == -1)
 | 
						|
		printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Open, load a file into memory, and close it. Check that the available space
 | 
						|
 * is sufficient to store the entire file. Return the bytes actually read from
 | 
						|
 * the file as seen by the read function. The verbose flag enables some extra
 | 
						|
 * printing of successful read status.
 | 
						|
 */
 | 
						|
int smh_load(const char *fname, void *memp, int avail, int verbose)
 | 
						|
{
 | 
						|
	long ret;
 | 
						|
	long fd;
 | 
						|
	size_t len;
 | 
						|
 | 
						|
	ret = -1;
 | 
						|
 | 
						|
	debug("%s: fname \'%s\', avail %u, memp %p\n", __func__, fname,
 | 
						|
	      avail, memp);
 | 
						|
 | 
						|
	/* Open the file */
 | 
						|
	fd = smh_open(fname, "rb");
 | 
						|
	if (fd == -1)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	/* Get the file length */
 | 
						|
	ret = smh_len_fd(fd);
 | 
						|
	if (ret == -1) {
 | 
						|
		smh_close(fd);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Check that the file will fit in the supplied buffer */
 | 
						|
	if (ret > avail) {
 | 
						|
		printf("%s: ERROR ret %ld, avail %u\n", __func__, ret,
 | 
						|
		       avail);
 | 
						|
		smh_close(fd);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	len = ret;
 | 
						|
 | 
						|
	/* Read the file into the buffer */
 | 
						|
	ret = smh_read(fd, memp, len);
 | 
						|
	if (ret == 0) {
 | 
						|
		/* Print successful load information if requested */
 | 
						|
		if (verbose) {
 | 
						|
			printf("\n%s\n", fname);
 | 
						|
			printf("    0x%8p dest\n", memp);
 | 
						|
			printf("    0x%08lx size\n", len);
 | 
						|
			printf("    0x%08x avail\n", avail);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Close the file */
 | 
						|
	smh_close(fd);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Get the file length from the filename
 | 
						|
 */
 | 
						|
long smh_len(const char *fname)
 | 
						|
{
 | 
						|
	long ret;
 | 
						|
	long fd;
 | 
						|
	long len;
 | 
						|
 | 
						|
	debug("%s: file \'%s\'\n", __func__, fname);
 | 
						|
 | 
						|
	/* Open the file */
 | 
						|
	fd = smh_open(fname, "rb");
 | 
						|
	if (fd < 0)
 | 
						|
		return fd;
 | 
						|
 | 
						|
	/* Get the file length */
 | 
						|
	len = smh_len_fd(fd);
 | 
						|
	if (len < 0) {
 | 
						|
		smh_close(fd);
 | 
						|
		return len;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Close the file */
 | 
						|
	ret = smh_close(fd);
 | 
						|
	if (ret < 0)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	debug("%s: returning len %ld\n", __func__, len);
 | 
						|
 | 
						|
	/* Return the file length (or -1 error indication) */
 | 
						|
	return len;
 | 
						|
}
 |