summaryrefslogtreecommitdiff
path: root/os/boot/mpc/flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/boot/mpc/flash.c')
-rw-r--r--os/boot/mpc/flash.c212
1 files changed, 212 insertions, 0 deletions
diff --git a/os/boot/mpc/flash.c b/os/boot/mpc/flash.c
new file mode 100644
index 00000000..d05f638f
--- /dev/null
+++ b/os/boot/mpc/flash.c
@@ -0,0 +1,212 @@
+#include "boot.h"
+
+typedef struct Flashdev Flashdev;
+struct Flashdev {
+ uchar* base;
+ int size;
+ uchar* exec;
+ char* config;
+ int conflen;
+};
+
+enum {
+ FLASHSEG = 256*1024,
+ CONFIGLIM = FLASHSEG,
+ BOOTOFF = FLASHSEG,
+ BOOTLEN = 3*FLASHSEG, /* third segment might be filsys */
+ /* rest of flash is free */
+};
+
+static Flashdev flash;
+
+/*
+ * configuration data is written between the bootstrap and
+ * the end of region 0. the region ends with allocation descriptors
+ * of the following form:
+ *
+ * byte order is big endian
+ *
+ * the last valid region found that starts with the string "#plan9.ini\n" is plan9.ini
+ */
+typedef struct Flalloc Flalloc;
+struct Flalloc {
+ ulong check; /* checksum of data, or ~0 */
+ ulong base; /* base of region; ~0 if unallocated, 0 if deleted */
+ uchar len[3];
+ uchar tag; /* see below */
+ uchar sig[4];
+};
+
+enum {
+ /* tags */
+ Tdead= 0,
+ Tboot= 0x01, /* space reserved for boot */
+ Tconf= 0x02, /* configuration data */
+ Tnone= 0xFF,
+
+ Noval= ~0,
+};
+
+static char flashsig[] = {0xF1, 0xA5, 0x5A, 0x1F};
+static char conftag[] = "#plan9.ini\n";
+
+static ulong
+checksum(uchar* p, int n)
+{
+ ulong s;
+
+ for(s=0; --n >= 0;)
+ s += *p++;
+ return s;
+}
+
+static int
+validptr(Flalloc *ap, uchar *p)
+{
+ return p > (uchar*)&end && p < (uchar*)ap;
+}
+
+static int
+flashcheck(Flalloc *ap, char **val, int *len)
+{
+ uchar *base;
+ int n;
+
+ if(ap->base == Noval || ap->base >= FLASHSEG || ap->tag == Tnone)
+ return 0;
+ base = flash.base+ap->base;
+ if(!validptr(ap, base))
+ return 0;
+ n = (((ap->len[0]<<8)|ap->len[1])<<8)|ap->len[2];
+ if(n == 0xFFFFFF)
+ n = 0;
+ if(n < 0)
+ return 0;
+ if(n > 0 && !validptr(ap, base+n-1))
+ return 0;
+ if(ap->check != Noval && checksum(base, n) != ap->check){
+ print("flash: bad checksum\n");
+ return 0;
+ }
+ *val = (char*)base;
+ *len = n;
+ return 1;
+}
+
+int
+flashinit(void)
+{
+ int len;
+ char *val;
+ Flalloc *ap;
+ void *addr;
+ long mbytes;
+ char type[20];
+
+ flash.base = 0;
+ flash.exec = 0;
+ flash.size = 0;
+ if(archflashreset(type, &addr, &mbytes) < 0){
+ print("flash: flash not present or not enabled\n"); /* shouldn't happen */
+ return 0;
+ }
+ flash.size = mbytes;
+ flash.base = addr;
+ flash.exec = flash.base + BOOTOFF;
+ flash.config = nil;
+ flash.conflen = 0;
+
+ for(ap = (Flalloc*)(flash.base+CONFIGLIM)-1; memcmp(ap->sig, flashsig, 4) == 0; ap--){
+ if(0)
+ print("conf #%8.8lux: #%x #%6.6lux\n", ap, ap->tag, ap->base);
+ if(ap->tag == Tconf &&
+ flashcheck(ap, &val, &len) &&
+ len >= sizeof(conftag)-1 &&
+ memcmp(val, conftag, sizeof(conftag)-1) == 0){
+ flash.config = val;
+ flash.conflen = len;
+ if(0)
+ print("flash: found config %8.8lux(%d):\n%s\n", val, len, val);
+ }
+ }
+ if(flash.config == nil)
+ print("flash: no config\n");
+ else
+ print("flash config %8.8lux(%d):\n%s\n", flash.config, flash.conflen, flash.config);
+ if(issqueezed(flash.exec) == Q_MAGIC){
+ print("flash: squeezed powerpc kernel installed\n");
+ return 1<<0;
+ }
+ if(GLLONG(flash.exec) == Q_MAGIC){
+ print("flash: unsqueezed powerpc kernel installed\n");
+ return 1<<0;
+ }
+ flash.exec = 0;
+ print("flash: no powerpc kernel in Flash\n");
+ return 0;
+}
+
+char*
+flashconfig(int)
+{
+ return flash.config;
+}
+
+int
+flashbootable(int)
+{
+ return flash.exec != nil && (issqueezed(flash.exec) || GLLONG(flash.exec) == Q_MAGIC);
+}
+
+int
+flashboot(int)
+{
+ ulong entry, addr;
+ void (*b)(void);
+ Exec *ep;
+ Block in;
+ long n;
+ uchar *p;
+
+ if(flash.exec == 0)
+ return -1;
+ p = flash.exec;
+ if(GLLONG(p) == Q_MAGIC){
+ /* unsqueezed: copy data and perhaps text, then jump to it */
+ ep = (Exec*)p;
+ entry = PADDR(GLLONG(ep->entry));
+ p += sizeof(Exec);
+ addr = entry;
+ n = GLLONG(ep->text);
+ if(addr != (ulong)p){
+ memmove((void*)addr, p, n);
+ print("text: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+ }
+ p += n;
+ if(entry >= FLASHMEM)
+ addr = 3*BY2PG; /* kernel text is in Flash, data in RAM */
+ else
+ addr = PGROUND(addr+n);
+ n = GLLONG(ep->data);
+ memmove((void*)addr, p, n);
+ print("data: %8.8lux <- %8.8lux [%ld]\n", addr, p, n);
+ }else{
+ in.data = p;
+ in.rp = in.data;
+ in.lim = p+BOOTLEN;
+ in.wp = in.lim;
+ n = unsqueezef(&in, &entry);
+ if(n < 0)
+ return -1;
+ }
+ print("entry=0x%lux\n", entry);
+ uartwait();
+ scc2stop();
+ /*
+ * Go to new code. It's up to the program to get its PC relocated to
+ * the right place.
+ */
+ b = (void (*)(void))KADDR(PADDR(entry));
+ (*b)();
+ return -1;
+}