summaryrefslogtreecommitdiff
path: root/os/boot/rpcg/zqs.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/boot/rpcg/zqs.c')
-rw-r--r--os/boot/rpcg/zqs.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/os/boot/rpcg/zqs.c b/os/boot/rpcg/zqs.c
new file mode 100644
index 00000000..b6296786
--- /dev/null
+++ b/os/boot/rpcg/zqs.c
@@ -0,0 +1,234 @@
+#include "boot.h"
+#include "squeeze.h"
+
+/*
+ * for details of `unsqueeze' see:
+ *
+ * %A Mark Taunton
+ * %T Compressed Executables: An Exercise in Thinking Small
+ * %P 385-404
+ * %I USENIX
+ * %B USENIX Conference Proceedings
+ * %D Summer 1991
+ * %C Nashville, TN
+ *
+ * several of the unimplemented improvements described in the paper
+ * have been implemented here
+ *
+ * there is a further transformation on the powerpc (QFLAG!=0) to shuffle bits
+ * in certain instructions so as to push the fixed bits to the top of the word.
+ */
+
+#define EXECHDRLEN (8*4)
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+ int n;
+ ulong tab[7*256];
+};
+
+#define GET4(p) (((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+
+/*
+ * for speed of unsqueezing from Flash, certain checks are
+ * not done inside the loop (as they would be in the unsqueeze program zqs),
+ * but instead the checksum is expected to catch corrupted files.
+ * in fact the Squeeze array bounds can't be exceeded in practice
+ * because the tables are always full for a squeezed kernel.
+ */
+enum {
+ QFLAG = 1, /* invert powerpc-specific code transformation */
+ CHECK = 0, /* check precise bounds in Squeeze array (otherwise checksum detects error) */
+};
+
+static ulong chksum;
+static int rdtab(Block*, Squeeze*, int);
+static ulong* unsqueeze(ulong*, uchar*, uchar*, Squeeze*, Squeeze*, ulong);
+static uchar* unsqzseg(uchar*, Block*, long, long, char*);
+static Alarm* unsqzal;
+
+int
+issqueezed(uchar *b)
+{
+ return GET4(b) == SQMAGIC? GET4(b+SQHDRLEN): 0;
+}
+
+static void
+unsqzdot(Alarm*)
+{
+ unsqzal = alarm(500, unsqzdot, nil);
+ print(".");
+}
+
+long
+unsqueezef(Block *b, ulong *entryp)
+{
+ uchar *loada, *wp;
+ ulong toptxt, topdat, oldsum;
+ long asis, nst, nsd;
+ Sqhdr *sqh;
+ Exec *ex;
+
+ if(BLEN(b) < SQHDRLEN+EXECHDRLEN)
+ return -1;
+ sqh = (Sqhdr*)b->rp;
+ if(GET4(sqh->magic) != SQMAGIC)
+ return -1;
+ chksum = 0;
+ toptxt = GET4(sqh->toptxt);
+ topdat = GET4(sqh->topdat);
+ oldsum = GET4(sqh->sum);
+ asis = GET4(sqh->asis);
+ nst = GET4(sqh->text);
+ nsd = GET4(sqh->data);
+ b->rp += SQHDRLEN;
+ ex = (Exec*)b->rp;
+ if(GET4(ex->magic) != Q_MAGIC){
+ print("zqs: not powerPC executable\n");
+ return -1;
+ }
+ *entryp = GET4(ex->entry);
+ b->rp += EXECHDRLEN;
+ loada = KADDR(PADDR(*entryp));
+ wp = unsqzseg(loada, b, nst, toptxt, "text");
+ if(wp == nil){
+ print("zqs: format error\n");
+ return -1;
+ }
+ if(nsd){
+ wp = (uchar*)PGROUND((ulong)wp);
+ wp = unsqzseg(wp, b, nsd, topdat, "data");
+ if(wp == nil){
+ print("zqs: format error\n");
+ return -1;
+ }
+ }
+ if(asis){
+ memmove(wp, b->rp, asis);
+ wp += asis;
+ b->rp += asis;
+ }
+ if(chksum != oldsum){
+ print("\nsqueezed kernel: checksum error: %8.8lux need %8.8lux\n", chksum, oldsum);
+ return -1;
+ }
+ return wp-loada;
+}
+
+static uchar *
+unsqzseg(uchar *wp, Block *b, long ns, long top, char *what)
+{
+ static Squeeze sq3, sq4;
+
+ print("unpack %s %8.8lux %lud:", what, wp, ns);
+ if(ns == 0)
+ return wp;
+ if(rdtab(b, &sq3, 0) < 0)
+ return nil;
+ if(rdtab(b, &sq4, 8) < 0)
+ return nil;
+ if(BLEN(b) < ns){
+ print(" **size error\n");
+ return nil;
+ }
+ unsqzal = alarm(500, unsqzdot, nil);
+ wp = (uchar*)unsqueeze((ulong*)wp, b->rp, b->rp+ns, &sq3, &sq4, top);
+ cancel(unsqzal);
+ unsqzal = nil;
+ print("\n");
+ if(wp == nil){
+ print("zqs: corrupt squeezed data stream\n");
+ return nil;
+ }
+ b->rp += ns;
+ return wp;
+}
+
+static ulong*
+unsqueeze(ulong *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top)
+{
+ ulong nx, csum;
+ int code, n;
+
+ if(QFLAG){
+ QREMAP(top); /* adjust top just once, outside the loop */
+ }
+ csum = chksum;
+ while(rp < ep){
+ /* no function calls within this loop for speed */
+ code = *rp;
+ rp++;
+ n = 0;
+ nx = code>>4;
+ do{
+ if(nx == 0){
+ nx = top;
+ }else{
+ if(nx==1){
+ nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0];
+ rp += 4;
+ }else if(nx <= 8){ /* 2 to 8 */
+ nx = ((nx-2)<<8) | rp[0];
+ if(CHECK && nx >= sq4->n)
+ return nil; /* corrupted file */
+ nx = sq4->tab[nx] | rp[1];
+ rp += 2;
+ }else{ /* 9 to 15 */
+ nx = ((nx-9)<<8) | rp[0];
+ if(CHECK && nx >= sq3->n)
+ return nil; /* corrupted file */
+ nx = sq3->tab[nx];
+ rp++;
+ }
+ if(rp > ep)
+ return nil; /* corrupted file */
+ if(QFLAG){
+ QREMAP(nx);
+ }
+ }
+ *wp = nx;
+ wp++;
+ csum += nx;
+ nx = code & 0xF;
+ }while(++n == 1);
+ }
+ chksum = csum;
+ return wp;
+}
+
+static int
+rdtab(Block *b, Squeeze *sq, int shift)
+{
+ uchar *p, *ep;
+ ulong v, w;
+ int i;
+
+ if(BLEN(b) < 2)
+ return -1;
+ i = (b->rp[0]<<8) | b->rp[1];
+ if(1)
+ print(" T%d", i);
+ b->rp += 2;
+ if((i -= 2) > 0){
+ if(BLEN(b) < i)
+ return -1;
+ }
+ sq->n = 0;
+ p = b->rp;
+ ep = b->rp+i;
+ b->rp += i;
+ v = 0;
+ while(p < ep){
+ w = 0;
+ do{
+ if(p >= ep)
+ return -1;
+ w = (w<<7) | (*p & 0x7F);
+ }while(*p++ & 0x80);
+ v += w;
+ if(0)
+ print("%d %8.8lux %8.8lux\n", sq->n, v, w);
+ sq->tab[sq->n++] = v<<shift;
+ }
+ return 0;
+}