Skip to content
Snippets Groups Projects
semihosting.c 4.91 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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
    
    
    static int smh_read(int fd, void *memp, int len);
    static int smh_open(const char *fname, char *modestr);
    static int smh_close(int fd);
    static int smh_len_fd(int fd);
    
    
    /*
     * Call the handler
     */
    static int smh_trap(unsigned int sysnum, void *addr)
    {
    	register int 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, 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)
    {
    	int ret, fd, 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 ret;
    
    	/* Get the file length */
    	ret = smh_len_fd(fd);
    	if (ret == -1) {
    		smh_close(fd);
    		return ret;
    	}
    
    	/* Check that the file will fit in the supplied buffer */
    	if (ret > avail) {
    		printf("%s: ERROR ret %d, avail %u\n", __func__, ret,
    		       avail);
    		smh_close(fd);
    		return ret;
    	}
    
    	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%08x size\n", len);
    			printf("    0x%08x avail\n", avail);
    		}
    	}
    
    	/* Close the file */
    	smh_close(fd);
    
    	return ret;
    }
    
    /*
     * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
     */
    
    static int smh_read(int fd, void *memp, int len)
    
    {
    	int ret;
    	struct smh_read_s {
    		int fd;
    		void *memp;
    		int len;
    	} read;
    
    	debug("%s: fd %d, memp %p, len %d\n", __func__, fd, memp, len);
    
    	read.fd = fd;
    	read.memp = memp;
    	read.len = len;
    
    	ret = smh_trap(SYSREAD, &read);
    	if (ret == 0) {
    		return 0;
    	} else {
    		/*
    		 * 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 %d, fd %d, len %u memp %p\n",
    		       __func__, ret, fd, len, memp);
    	}
    	return ret;
    }
    
    /*
     * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
     * descriptor or -1 on error.
     */
    
    static int smh_open(const char *fname, char *modestr)
    
    {
    	int ret, fd, mode;
    	struct smh_open_s {
    		const char *fname;
    		unsigned int mode;
    		unsigned int len;
    	} open;
    
    	debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
    
    	ret = -1;
    
    	/* 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 ret;
    	}
    
    	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 %d for file \'%s\'\n", __func__, fd,
    		       fname);
    
    	return fd;
    }
    
    /*
     * Close the file using the file descriptor
     */
    
    static int smh_close(int fd)
    
    {
    	int ret;
    	long fdlong;
    
    	debug("%s: fd %d\n", __func__, fd);
    
    	fdlong = (long)fd;
    	ret = smh_trap(SYSCLOSE, &fdlong);
    	if (ret == -1)
    		printf("%s: ERROR fd %d\n", __func__, fd);
    
    	return ret;
    }
    
    /*
     * Get the file length from the file descriptor
     */
    
    static int smh_len_fd(int fd)
    
    {
    	int ret;
    	long fdlong;
    
    	debug("%s: fd %d\n", __func__, fd);
    
    	fdlong = (long)fd;
    	ret = smh_trap(SYSFLEN, &fdlong);
    	if (ret == -1)
    		printf("%s: ERROR ret %d\n", __func__, ret);
    
    	return ret;
    }
    
    /*
     * Get the file length from the filename
     */
    int smh_len(const char *fname)
    {
    	int ret, fd, len;
    
    	debug("%s: file \'%s\'\n", __func__, fname);
    
    	/* Open the file */
    	fd = smh_open(fname, "rb");
    	if (fd == -1)
    		return fd;
    
    	/* Get the file length */
    	len = smh_len_fd(fd);
    
    	/* Close the file */
    	ret = smh_close(fd);
    	if (ret == -1)
    		return ret;
    
    	debug("%s: returning len %d\n", __func__, len);
    
    	/* Return the file length (or -1 error indication) */
    	return len;
    }