aboutsummaryrefslogtreecommitdiff
path: root/snilib/sniprom.c
blob: fa5acc13a9a557c5a6a220f17b86dbd8c1ca4a0d (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

#include <subarch.h>
#include "sniprom.h"

#define MAX_ARG	64

static char	*iargv[MAX_ARG];
static int	iargc=0;
static char	*label=NULL;

extern unsigned long _end;

void prom_wait(const char *prompt) {
	printf("%s: called\n\r", __FUNCTION__);
}

void prom_fatal(const char *message, ...) {
	va_list ap;

	if (message != NULL) {
		printf("FATAL ERROR:  ");
		va_start(ap, message);
		vprintf(message, ap);
		va_end(ap);
	}

	prom_wait("\n\r--- Press <spacebar> to enter ARC interactive mode ---");
	__prom_reset();
}

int (*__prom_lseek)(FILE, int, int)   = NULL;


/*
 * The SNI Prom does not contain an official entry point for "prom_seek" which is
 * a pain and basically is a showstopper for bootloaders. There is a seek function
 * though which seems to be inbetween prom_open and prom_read. We try to find
 * it by looking for the function prolog e.g. the $sp setup. We then know the
 * address of the seek.		flo@rfc822.org 2007-08-13
 */

void prom_init(void ) {
	unsigned int	jump;
	unsigned int	*readaddr,
			*openaddr,
			*saddr;

	jump = *((unsigned int *)PROM_ENTRY(PROM_READ));
	readaddr=(unsigned int *) (((jump & 0x03ffffff)<<2)|0xb0000000);
	jump = *((unsigned int *)PROM_ENTRY(PROM_OPEN));
	openaddr= (unsigned int *) (((jump & 0x03ffffff)<<2)|0xb0000000);

	for(saddr=readaddr-1;saddr>openaddr;saddr--) {
		/* Search for addiu $sp, negative */
		if ((*saddr & 0xffff8000) == 0x27bd8000) {
			__prom_lseek=(void *) saddr;
			break;
		}
	}

	if (__prom_lseek == NULL)
		prom_fatal("Didnt find prom_seek prolog between open and read\n\r");
}


/* Give 1MByte of memory behind the arcboot to the allocator */
void prom_init_malloc(unsigned long loadaddr, unsigned long size) {
	malloc_area_add((unsigned long) &_end, 0x100000);
}

int prom_seek(FILE stream, long long position, int whence) {
	int	pos32=position, res;

	res=__prom_lseek(stream, pos32, whence);

	if (res != position)
		prom_fatal("Seek failed\n\r");

	return 0;
}

void prom_restart(void ) {
	printf("%s: called\n\r", __FUNCTION__);
}

void prom_flush_cache_all(void ) {
	__prom_flushcache();
}

void prom_return_interactive(void ) {
	printf("%s: called\n\r", __FUNCTION__);
}

int prom_write(FILE stream, char *buf, unsigned long len, unsigned long *rlen) {
	int	res;
	/* SNI returns no status but standard conform length */
	res=__prom_write(stream, buf, len);
	*rlen=res;
	return res<0;
}

/*
 * We have seen larger reads to fail on RM200C Prom Version 5
 * and RM400 Prom Version 4. It seems this is secondary cache related. As we
 * would like to use the read-ahead and cache we simply split the reads into
 * smaller chunks here.
 *
 */
int prom_read(FILE stream, char *buf, unsigned long len, unsigned long *rlen) {
	int	res=0,i;
	int	read=0, thisread;

	while(read < len) {
		int left=len-read;
		if (left > 4096)
			thisread=4096;
		else
			thisread=left;

		res=__prom_read(stream, buf+read, thisread);

		read+=thisread;

		if (res < thisread)
			break;
	}
	*rlen=read;

	return (len > read);
}

int prom_open(char *name, int mode, FILE *stream) {
	int	res;
	res=__prom_open(name, mode);
	*stream=res;
	return 0;
}

int prom_close(FILE stream) {
	return 0;
}

void prom_add_arg(char *arg) {
	iargv[iargc++]=strdup(arg);
}

/*
 * dkncr(0,0,10) label cmd line arg
 * Kernel drops argv[0] so we need to add dkncr... and drop the label
 *
 */
int prom_parse_args(int argc, char **argv) {
	int	i;
	prom_add_arg(argv[0]);
	if (argc > 1)
		label=strdup(argv[1]);
	for(i=2;i<argc;i++ )
		prom_add_arg(argv[i]);
	return iargc;
}

char *prom_get_label(void ) {
	return (label) ? label : "linux";
}

char *prom_get_partition(void ) {
	return "dkncr(0,0,1)";
}

char *prom_get_options(void ) {
	return NULL;
}

void prom_get_karg(int *argc, char ***argv) {
	*argc=iargc;
	*argv=iargv;
}