aboutsummaryrefslogtreecommitdiff
path: root/snilib/sniprom.c
diff options
context:
space:
mode:
Diffstat (limited to 'snilib/sniprom.c')
-rw-r--r--snilib/sniprom.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/snilib/sniprom.c b/snilib/sniprom.c
new file mode 100644
index 0000000..fa5acc1
--- /dev/null
+++ b/snilib/sniprom.c
@@ -0,0 +1,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;
+}