summaryrefslogtreecommitdiffstats
path: root/data/sysrom/exec.c
blob: aa25c1ee0e848e6a79bfbfe836f2fb5bb85b2705 (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
/* ----------------------------------------------------------------------- *
 *
 *   Copyright 2010 H. Peter Anvin - All Rights Reserved
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 *   Boston MA 02110-1301, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/*
 * exec.c
 *
 * Run an executable
 */

#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <ioreg.h>
#include "sysrom.h"
#include "ff.h"

#define EXEC_MAGIC	0x1ae475e6

struct exec_header {
	uint32_t magic;
	uint32_t hdr_len;
	uint32_t flags;
	uint32_t entrypoint;
	uint32_t totalsize;
	uint32_t binsize;
	uint32_t bsssize;
	uint32_t nrelocs;
};

int exec_file(const char *filename, void *addr,
	      int argc, char **argv, char **envp)
{
	FRESULT fr;
	FIL file;
	UINT bytesread;
	struct exec_header hdr;
	uint32_t nrel;
	int (*start)(int, char **, char **);

	fr = f_open(&file, filename, FA_READ|FA_OPEN_EXISTING);
	if (fr != FR_OK)
		return -1;

	fr = f_read(&file, &hdr, sizeof hdr, &bytesread);
	if (fr != FR_OK || bytesread != sizeof hdr)
		goto err;

	fr = f_lseek(&file, hdr.hdr_len);
	if (fr != FR_OK)
		goto err;

	if (hdr.magic != EXEC_MAGIC ||
	    (hdr.flags & 0xffff) || /* Incompatible flags */
	    hdr.binsize + hdr.bsssize > hdr.totalsize)
		goto err;

	fr = f_read(&file, addr, hdr.binsize, &bytesread);
	if (fr != FR_OK || bytesread != hdr.binsize)
		goto err;

	/* Zero the BSS */
	memset((char *)addr + hdr.binsize, 0, hdr.bsssize);

	/* Process relocations */
	nrel = hdr.nrelocs;
	while (nrel) {
		size_t reloc_buf[128];
		size_t *relp = reloc_buf;
		unsigned int xrel = (nrel < 128) ? nrel : 128;
		unsigned int bytes = xrel * sizeof(size_t);

		nrel -= xrel;

		fr = f_read(&file, reloc_buf, bytes, &bytesread);
		if (fr != FR_OK || bytesread != bytes)
			goto err;

		while (xrel--) {
			uint32_t rel = *relp++;
			if (rel + 4 > hdr.totalsize || (rel & 1))
				goto err;

			*(size_t *)((char *)addr + rel) += (size_t)addr;
		}
	}

	f_close(&file);

	start = (int (*)(int, char **, char **))
	  ((const char *)addr + hdr.entrypoint);
	return start(argc, argv, envp) & 0xff;

err:
	f_close(&file);
	return -1;
}

/*
 * Try to load abc8000.sys, and run it if it exists
 */
static FATFS sd_fs;
extern char __end_of_clear[];	/* Beginning of "free memory" */

void disk_boot(void)
{
	static const char boot_filename[] = "abc8000.sys";
	char * const load_addr = __end_of_clear;

	printf("Mounting SD card...\n");
	f_mount(0, &sd_fs);

	printf("Running boot file @ %p...\n", load_addr);
	if (exec_file(boot_filename, load_addr, 0, NULL, NULL))
		printf("Boot file execution failed!\n");
}