changeset 865:f5affe83ba2d

lldbg hack (poor girl's substitute for JTAG) implemented
author Space Falcon <falcon@ivan.Harhan.ORG>
date Fri, 15 May 2015 00:02:03 +0000
parents 4fa939eada22
children 3adb4154f02f
files gsm-fw/Makefile gsm-fw/cfgmagic/feature.lldbg gsm-fw/cfgmagic/processconf.sh gsm-fw/finlink/Makefile gsm-fw/lldbg/Makefile gsm-fw/lldbg/README gsm-fw/lldbg/cmd_dump.c gsm-fw/lldbg/cmd_r16.c gsm-fw/lldbg/cmd_r32.c gsm-fw/lldbg/cmd_r8.c gsm-fw/lldbg/cmd_w16.c gsm-fw/lldbg/cmd_w32.c gsm-fw/lldbg/cmd_w8.c gsm-fw/lldbg/cmdentry.c gsm-fw/lldbg/cmdtab.c gsm-fw/lldbg/cmdtab.h gsm-fw/lldbg/dispatch.c gsm-fw/lldbg/doprnt.c gsm-fw/lldbg/entry.S gsm-fw/lldbg/entryinfo.c gsm-fw/lldbg/getchar.c gsm-fw/lldbg/hexarg.c gsm-fw/lldbg/main.c gsm-fw/lldbg/ns16550.h gsm-fw/lldbg/parseargs.c gsm-fw/lldbg/printf.c gsm-fw/lldbg/putchar.c gsm-fw/lldbg/serio.S gsm-fw/lldbg/types.h
diffstat 29 files changed, 999 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/gsm-fw/Makefile	Sun May 10 19:35:29 2015 +0000
+++ b/gsm-fw/Makefile	Fri May 15 00:02:03 2015 +0000
@@ -1,5 +1,5 @@
 SUBDIR=	L1 bsp ccd comlib finlink g23m-aci g23m-glue g23m-gsm gpf include \
-	libiram nucleus riviera serial services sprintf sysglue
+	libiram lldbg nucleus riviera serial services sprintf sysglue
 
 default:	config.stamp
 	${MAKE} ${MFLAGS} -f Makefile.build $@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/cfgmagic/feature.lldbg	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,3 @@
+CONFIG_LLDBG=1
+export_to_c	CONFIG_LLDBG CONFIG_LLDBG_UART_BASE
+export_to_mk	CONFIG_LLDBG
--- a/gsm-fw/cfgmagic/processconf.sh	Sun May 10 19:35:29 2015 +0000
+++ b/gsm-fw/cfgmagic/processconf.sh	Fri May 15 00:02:03 2015 +0000
@@ -53,8 +53,10 @@
 # Serial configuration
 case "$RVTMUX_UART_port" in
 	IrDA)
+		CONFIG_LLDBG_UART_BASE=0xFFFF5000
 		;;
 	MODEM)
+		CONFIG_LLDBG_UART_BASE=0xFFFF5800
 		CONFIG_RVTMUX_ON_MODEM=1
 		export_to_c CONFIG_RVTMUX_ON_MODEM
 		;;
@@ -118,6 +120,10 @@
 	BUILD_COMPONENTS="$BUILD_COMPONENTS ccd comlib"
 	BUILD_COMPONENTS="$BUILD_COMPONENTS g23m-aci g23m-glue g23m-gsm"
 fi
+if [ "$CONFIG_LLDBG" = 1 ]
+then
+	BUILD_COMPONENTS="$BUILD_COMPONENTS lldbg"
+fi
 
 export_to_mk BUILD_COMPONENTS
 
--- a/gsm-fw/finlink/Makefile	Sun May 10 19:35:29 2015 +0000
+++ b/gsm-fw/finlink/Makefile	Fri May 15 00:02:03 2015 +0000
@@ -53,6 +53,9 @@
 ifeq (${RVM_ETM_SWE},1)
 EXT_PIECES+=	../services/etm/xipcode.o
 endif
+ifeq (${CONFIG_LLDBG},1)
+EXT_PIECES+=	../lldbg/xipcode.o
+endif
 
 flashImage:	${FLASH_TARGET}
 ramImage:	${RAM_TARGET}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/Makefile	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,16 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin -mthumb-interwork -mthumb
+ASFLAGS=-mthumb-interwork
+LD=	arm-elf-ld
+
+OBJS=	cmd_dump.o cmd_r16.o cmd_r32.o cmd_r8.o cmd_w16.o cmd_w32.o cmd_w8.o \
+	cmdentry.o cmdtab.o dispatch.o doprnt.o entry.o entryinfo.o getchar.o \
+	hexarg.o main.o parseargs.o printf.o putchar.o serio.o
+
+all:	xipcode.o
+
+xipcode.o:	${OBJS}
+	${LD} -r -o $@ ${OBJS}
+
+clean:
+	rm -f *.[oa] *errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/README	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,30 @@
+The hack implemented in this directory is a poor girl's substitute for JTAG,
+concocted for the purpose of debugging hard crashes in the firmware.  The
+intended usage is that the developer seeking to troubleshoot perplexing
+misbehavior in the firmware inserts a call to lldbg_entry() as a form of
+breakpoint at the earliest place in the main fw where it is seen that something
+has gone astray, and when the thread of code execution hits this lldbg_entry()
+function, the regular Nucleus environment gets completely frozen.  Lldbg entry
+code saves all registers and the return address (LR) on whatever stack it is
+was called on, then disables all interrupts and switches to its own stack which
+is not used by any other part of the fw.
+
+This lldbg code is linked together with the main fw when feature lldbg is
+enabled in build.conf, but it stands as its own separate body.  All functions
+and variables within lldbg are prefixed with lldbg_ to avoid any clashes, and
+once this lldbg code gains control, it does everything on its own without making
+use of *any* part of the regular fw.  The code that forms lldbg is based on
+FreeCalypso target-utils (loadagent) and is fully independent of the regular fw.
+
+Lldbg uses its own UART input and output code as well: the entry code waits for
+any previous output (presumably RVTMUX) to go out, then sends out an STX-wrapped
+message to make the lldbg entry visible in rvtdump/rvinterf, preceded by 3 STX
+characters to terminate any RVTMUX packet in progress.  From this point onward
+all further communication is done in an ASCII terminal fashion: upon seeing the
+lldbg entry message, the user needs to kill rvtdump/rvinterf and switch to
+fc-serterm.  All lldbg code runs with interrupts disabled, thus UART input and
+output are polled.
+
+Once in lldbg mode, the user (developer) can execute various memory dump
+commands to see the frozen state of the fw upon lldbg entry, and hopefully
+figure out what went wrong earlier.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmd_dump.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,50 @@
+/*
+ * This is a human-oriented memory dump command.  The dump is given in
+ * both hex and ASCII, with readable spacing.
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+lldbg_cmd_dump(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long start, length;
+	u_long offset;
+	u_char intbuf[16];
+	int i, c;
+
+	if (lldbg_parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (lldbg_parse_hexarg(argv[0], 8, &start) < 0) {
+	      lldbg_printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (lldbg_parse_hexarg(argv[1], 8, &length) < 0) {
+    lldbg_printf("ERROR: arg2 must be a valid 32-bit hex value (length)\n");
+		return;
+	}
+	if (start & 0xF || length & 0xF) {
+    lldbg_printf("ERROR: implementation limit: 16-byte alignment required\n");
+		return;
+	}
+	for (offset = 0; offset < length; offset += 0x10) {
+		bcopy(start + offset, intbuf, 0x10);
+		lldbg_printf("%08X: ", start + offset);
+		for (i = 0; i < 16; i++) {
+			lldbg_printf("%02X ", intbuf[i]);
+			if ((i & 3) == 3)
+				lldbg_putchar(' ');
+		}
+		for (i = 0; i < 16; i++) {
+			c = intbuf[i];
+			if (c >= ' ' && c <= '~')
+				lldbg_putchar(c);
+			else
+				lldbg_putchar('.');
+		}
+		lldbg_putchar('\n');
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmd_r16.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,26 @@
+/*
+ * r16 hexaddr -- read a 16-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+lldbg_cmd_r16(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (lldbg_parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (lldbg_parse_hexarg(argv[0], 8, &addr) < 0) {
+	  lldbg_printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 1) {
+		lldbg_printf("ERROR: unaligned address\n");
+		return;
+	}
+	lldbg_printf("%04X\n", *(volatile u16 *)addr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmd_r32.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,26 @@
+/*
+ * r32 hexaddr -- read a 32-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+lldbg_cmd_r32(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (lldbg_parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (lldbg_parse_hexarg(argv[0], 8, &addr) < 0) {
+	  lldbg_printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 3) {
+		lldbg_printf("ERROR: unaligned address\n");
+		return;
+	}
+	lldbg_printf("%08X\n", *(volatile u32 *)addr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmd_r8.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,22 @@
+/*
+ * r8 hexaddr -- read an 8-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+lldbg_cmd_r8(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (lldbg_parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (lldbg_parse_hexarg(argv[0], 8, &addr) < 0) {
+	  lldbg_printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	lldbg_printf("%02X\n", *(volatile u8 *)addr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmd_w16.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,30 @@
+/*
+ * w16 hexaddr xxxx -- write a 16-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+lldbg_cmd_w16(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long addr, data;
+
+	if (lldbg_parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (lldbg_parse_hexarg(argv[0], 8, &addr) < 0) {
+	      lldbg_printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 1) {
+		lldbg_printf("ERROR: unaligned address\n");
+		return;
+	}
+	if (lldbg_parse_hexarg(argv[1], 4, &data) < 0) {
+		lldbg_printf("ERROR: arg2 must be a valid 16-bit hex value\n");
+		return;
+	}
+	*(volatile u16 *)addr = data;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmd_w32.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,30 @@
+/*
+ * w32 hexaddr xxxxxxxx -- write a 32-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+lldbg_cmd_w32(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long addr, data;
+
+	if (lldbg_parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (lldbg_parse_hexarg(argv[0], 8, &addr) < 0) {
+	      lldbg_printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 3) {
+		lldbg_printf("ERROR: unaligned address\n");
+		return;
+	}
+	if (lldbg_parse_hexarg(argv[1], 8, &data) < 0) {
+		lldbg_printf("ERROR: arg2 must be a valid 32-bit hex value\n");
+		return;
+	}
+	*(volatile u32 *)addr = data;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmd_w8.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,26 @@
+/*
+ * w8 hexaddr xx -- write an 8-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+lldbg_cmd_w8(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long addr, data;
+
+	if (lldbg_parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (lldbg_parse_hexarg(argv[0], 8, &addr) < 0) {
+	      lldbg_printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (lldbg_parse_hexarg(argv[1], 2, &data) < 0) {
+		lldbg_printf("ERROR: arg2 must be a valid 8-bit hex value\n");
+		return;
+	}
+	*(volatile u8 *)addr = data;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmdentry.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,72 @@
+/*
+ * This module implements ASCII command entry via the serial port,
+ * with normal echo and minimal editing (rubout and kill).
+ *
+ * The command string buffer is bss-allocated here as well.  It is
+ * sized to allow a maximum-size S-record to be sent as a command,
+ * as that is how we expect flash loading and XRAM chain-loading
+ * to be done.
+ */
+
+#define	MAXCMD	527
+
+char lldbg_command[MAXCMD+1];
+
+/*
+ * The command_entry() function takes no arguments, and begins by waiting
+ * for serial input - hence the prompt should be printed before calling it.
+ *
+ * This function returns when one of the following characters is received:
+ * CR - accepts the command
+ * ^C or ^U - cancels the command
+ *
+ * The return value is non-zero if a non-empty command was accepted with CR,
+ * or 0 if the user hit CR with no input or if the command was canceled
+ * with ^C or ^U.  In any case a CRLF is sent out the serial port
+ * to close the input echo line before this function returns.
+ */
+lldbg_command_entry()
+{
+	int inlen, ch;
+
+	for (inlen = 0; ; ) {
+		ch = lldbg_getchar();
+		if (ch >= ' ' && ch <= '~') {
+			if (inlen < MAXCMD) {
+				lldbg_command[inlen++] = ch;
+				lldbg_putchar(ch);
+			} else
+				/* putchar(7) */;
+			continue;
+		}
+		switch (ch) {
+		case '\r':
+		case '\n':
+			lldbg_command[inlen] = '\0';
+			lldbg_putchar('\n');
+			return(inlen);
+		case '\b':	/* BS */
+		case 0x7F:	/* DEL */
+			if (inlen) {
+				lldbg_putchar('\b');
+				lldbg_putchar(' ');
+				lldbg_putchar('\b');
+				inlen--;
+			} else
+				/* putchar(7) */;
+			continue;
+		case 0x03:	/* ^C */
+			lldbg_putchar('^');
+			lldbg_putchar('C');
+			lldbg_putchar('\n');
+			return(0);
+		case 0x15:	/* ^U */
+			lldbg_putchar('^');
+			lldbg_putchar('U');
+			lldbg_putchar('\n');
+			return(0);
+		default:
+			/* putchar(7) */;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmdtab.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,22 @@
+#include "cmdtab.h"
+
+extern void lldbg_cmd_dump();
+extern void lldbg_cmd_entry();
+extern void lldbg_cmd_r8();
+extern void lldbg_cmd_r16();
+extern void lldbg_cmd_r32();
+extern void lldbg_cmd_w8();
+extern void lldbg_cmd_w16();
+extern void lldbg_cmd_w32();
+
+const struct cmdtab lldbg_cmdtab[] = {
+	{"dump", lldbg_cmd_dump},
+	{"entry", lldbg_cmd_entry},
+	{"r8", lldbg_cmd_r8},
+	{"r16", lldbg_cmd_r16},
+	{"r32", lldbg_cmd_r32},
+	{"w8", lldbg_cmd_w8},
+	{"w16", lldbg_cmd_w16},
+	{"w32", lldbg_cmd_w32},
+	{0, 0}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/cmdtab.h	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,6 @@
+/* this structure is used for interactive command dispatch */
+
+struct cmdtab {
+	char	*cmd;
+	void	(*func)();
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/dispatch.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,32 @@
+/*
+ * This module implements the dispatch of interactively entered
+ * commands to their respective implementation functions via cmdtab.
+ */
+
+#include "cmdtab.h"
+
+extern char lldbg_command[];
+extern struct cmdtab lldbg_cmdtab[];
+
+void
+lldbg_command_dispatch()
+{
+	char *cp, *np;
+	struct cmdtab *tp;
+
+	for (cp = lldbg_command; *cp == ' '; cp++)
+		;
+	if (!*cp)
+		return;
+	for (np = cp; *cp && *cp != ' '; cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = lldbg_cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, np))
+			break;
+	if (tp->func)
+		tp->func(cp);
+	else
+		lldbg_printf("ERROR: unknown or unimplemented command\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/doprnt.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,298 @@
+/* the guts of printf - this implementation came from 4.3BSD-Tahoe */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+extern void lldbg_putchar();
+#define	PUTC	lldbg_putchar
+
+#define	ARG() \
+	_ulong = flags&LONGINT ? va_arg(argp, long) : va_arg(argp, int);
+
+#define	BUF	12
+
+#define	todigit(c)	((c) - '0')
+#define	tochar(n)	((n) + '0')
+
+#define	LONGINT		0x01		/* long integer */
+#define	LONGDBL		0x02		/* long double; unimplemented */
+#define	SHORTINT	0x04		/* short integer */
+#define	ALT		0x08		/* alternate form */
+#define	LADJUST		0x10		/* left adjustment */
+#define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
+#define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
+
+lldbg_doprnt(fmt0, argp, outfunc, outfunc_param)
+	u_char *fmt0;
+	va_list argp;
+	void (*outfunc)();
+	void *outfunc_param;
+{
+	register u_char *fmt;	/* format string */
+	register int ch;	/* character from fmt */
+	register int cnt;	/* return value accumulator */
+	register int n;		/* random handy integer */
+	register char *t;	/* buffer pointer */
+	u_long _ulong;		/* integer arguments %[diouxX] */
+	int base;		/* base for [diouxX] conversion */
+	int dprec;		/* decimal precision in [diouxX] */
+	int fieldsz;		/* field size expanded by sign, etc */
+	int flags;		/* flags as above */
+	int prec;		/* precision from format (%.3d), or -1 */
+	int realsz;		/* field size expanded by decimal precision */
+	int size;		/* size of converted field or string */
+	int width;		/* width from format (%8d), or 0 */
+	char sign;		/* sign prefix (' ', '+', '-', or \0) */
+	char softsign;		/* temporary negative sign for floats */
+	char *digs;		/* digits for [diouxX] conversion */
+	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
+
+	fmt = fmt0;
+	digs = "0123456789abcdef";
+	for (cnt = 0;; ++fmt) {
+		for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
+			PUTC(ch);
+		if (!ch)
+			return (cnt);
+
+		flags = 0; dprec = 0; width = 0;
+		prec = -1;
+		sign = '\0';
+
+rflag:		switch (*++fmt) {
+		case ' ':
+			/*
+			 * ``If the space and + flags both appear, the space
+			 * flag will be ignored.''
+			 *	-- ANSI X3J11
+			 */
+			if (!sign)
+				sign = ' ';
+			goto rflag;
+		case '#':
+			flags |= ALT;
+			goto rflag;
+		case '*':
+			/*
+			 * ``A negative field width argument is taken as a
+			 * - flag followed by a  positive field width.''
+			 *	-- ANSI X3J11
+			 * They don't exclude field widths read from args.
+			 */
+			if ((width = va_arg(argp, int)) >= 0)
+				goto rflag;
+			width = -width;
+			/* FALLTHROUGH */
+		case '-':
+			flags |= LADJUST;
+			goto rflag;
+		case '+':
+			sign = '+';
+			goto rflag;
+		case '.':
+			if (*++fmt == '*')
+				n = va_arg(argp, int);
+			else {
+				n = 0;
+				while (isascii(*fmt) && isdigit(*fmt))
+					n = 10 * n + todigit(*fmt++);
+				--fmt;
+			}
+			prec = n < 0 ? -1 : n;
+			goto rflag;
+		case '0':
+			/*
+			 * ``Note that 0 is taken as a flag, not as the
+			 * beginning of a field width.''
+			 *	-- ANSI X3J11
+			 */
+			flags |= ZEROPAD;
+			goto rflag;
+		case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			n = 0;
+			do {
+				n = 10 * n + todigit(*fmt);
+			} while (isascii(*++fmt) && isdigit(*fmt));
+			width = n;
+			--fmt;
+			goto rflag;
+		case 'L':
+			flags |= LONGDBL;
+			goto rflag;
+		case 'h':
+			flags |= SHORTINT;
+			goto rflag;
+		case 'l':
+			flags |= LONGINT;
+			goto rflag;
+		case 'c':
+			*(t = buf) = va_arg(argp, int);
+			size = 1;
+			sign = '\0';
+			goto pforw;
+		case 'D':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'd':
+		case 'i':
+			ARG();
+			if ((long)_ulong < 0) {
+				_ulong = -_ulong;
+				sign = '-';
+			}
+			base = 10;
+			goto number;
+		case 'n':
+			if (flags & LONGINT)
+				*va_arg(argp, long *) = cnt;
+			else if (flags & SHORTINT)
+				*va_arg(argp, short *) = cnt;
+			else
+				*va_arg(argp, int *) = cnt;
+			break;
+		case 'O':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'o':
+			ARG();
+			base = 8;
+			goto nosign;
+		case 'p':
+			/*
+			 * ``The argument shall be a pointer to void.  The
+			 * value of the pointer is converted to a sequence
+			 * of printable characters, in an implementation-
+			 * defined manner.''
+			 *	-- ANSI X3J11
+			 */
+			/* NOSTRICT */
+			_ulong = (u_long)va_arg(argp, void *);
+			base = 16;
+			goto nosign;
+		case 's':
+			if (!(t = va_arg(argp, char *)))
+				t = "(null)";
+			if (prec >= 0) {
+				/*
+				 * can't use strlen; can only look for the
+				 * NUL in the first `prec' characters, and
+				 * strlen() will go further.
+				 */
+				char *p;
+
+				for (p = t, size = 0; size < prec; p++, size++)
+					if (*p == '\0')
+						break;
+			} else
+				size = strlen(t);
+			sign = '\0';
+			goto pforw;
+		case 'U':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'u':
+			ARG();
+			base = 10;
+			goto nosign;
+		case 'X':
+			digs = "0123456789ABCDEF";
+			/* FALLTHROUGH */
+		case 'x':
+			ARG();
+			base = 16;
+			/* leading 0x/X only if non-zero */
+			if (flags & ALT && _ulong != 0)
+				flags |= HEXPREFIX;
+
+			/* unsigned conversions */
+nosign:			sign = '\0';
+			/*
+			 * ``... diouXx conversions ... if a precision is
+			 * specified, the 0 flag will be ignored.''
+			 *	-- ANSI X3J11
+			 */
+number:			if ((dprec = prec) >= 0)
+				flags &= ~ZEROPAD;
+
+			/*
+			 * ``The result of converting a zero value with an
+			 * explicit precision of zero is no characters.''
+			 *	-- ANSI X3J11
+			 */
+			t = buf + BUF;
+			if (_ulong != 0 || prec != 0) {
+				do {
+					*--t = digs[_ulong % base];
+					_ulong /= base;
+				} while (_ulong);
+				digs = "0123456789abcdef";
+				if (flags & ALT && base == 8 && *t != '0')
+					*--t = '0'; /* octal leading 0 */
+			}
+			size = buf + BUF - t;
+
+pforw:
+			/*
+			 * All reasonable formats wind up here.  At this point,
+			 * `t' points to a string which (if not flags&LADJUST)
+			 * should be padded out to `width' places.  If
+			 * flags&ZEROPAD, it should first be prefixed by any
+			 * sign or other prefix; otherwise, it should be blank
+			 * padded before the prefix is emitted.  After any
+			 * left-hand padding and prefixing, emit zeroes
+			 * required by a decimal [diouxX] precision, then print
+			 * the string proper, then emit zeroes required by any
+			 * leftover floating precision; finally, if LADJUST,
+			 * pad with blanks.
+			 */
+
+			/*
+			 * compute actual size, so we know how much to pad
+			 * fieldsz excludes decimal prec; realsz includes it
+			 */
+			fieldsz = size;
+			if (sign)
+				fieldsz++;
+			if (flags & HEXPREFIX)
+				fieldsz += 2;
+			realsz = dprec > fieldsz ? dprec : fieldsz;
+
+			/* right-adjusting blank padding */
+			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
+				for (n = realsz; n < width; n++)
+					PUTC(' ');
+			/* prefix */
+			if (sign)
+				PUTC(sign);
+			if (flags & HEXPREFIX) {
+				PUTC('0');
+				PUTC((char)*fmt);
+			}
+			/* right-adjusting zero padding */
+			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
+				for (n = realsz; n < width; n++)
+					PUTC('0');
+			/* leading zeroes from decimal precision */
+			for (n = fieldsz; n < dprec; n++)
+				PUTC('0');
+
+			for (n = size; --n >= 0; )
+				PUTC(*t++);
+			/* left-adjusting padding (always blank) */
+			if (flags & LADJUST)
+				for (n = realsz; n < width; n++)
+					PUTC(' ');
+			/* finally, adjust cnt */
+			cnt += width > realsz ? width : realsz;
+			break;
+		case '\0':	/* "%?" prints ?, unless ? is NULL */
+			return (cnt);
+		default:
+			PUTC((char)*fmt);
+			cnt++;
+		}
+	}
+	/* NOTREACHED */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/entry.S	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,23 @@
+	.section "ext.ram","aw",%nobits
+	.balign  4
+	.globl	lldbg_stack
+lldbg_stack:
+	.space	2048
+lldbg_init_sp:
+
+	.text
+	.code	32
+	.globl	lldbg_entry
+lldbg_entry:
+	stmfd	sp!, {r0-r12,lr}
+	mrs	r0, CPSR
+	mov	r1, sp
+	/* supervisor mode, disable all interrupts */
+	msr	CPSR_c, #0xd3
+	ldr	sp, =lldbg_init_sp
+	/* save entry SP and CPSR */
+	ldr	r2, =lldbg_entry_cpsr
+	str	r0, [r2]
+	ldr	r2, =lldbg_entry_sp
+	str	r1, [r2]
+	b	lldbg_main
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/entryinfo.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,11 @@
+#include "types.h"
+
+u32 lldbg_entry_cpsr;
+u32 lldbg_entry_sp;
+
+void
+lldbg_cmd_entry()
+{
+	lldbg_printf("CPSR on entry: %08X\n", lldbg_entry_cpsr);
+	lldbg_printf("SP on entry after register save: %08X\n", lldbg_entry_sp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/getchar.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,12 @@
+extern int lldbg_serial_in_poll();
+
+int
+lldbg_getchar()
+{
+	register int c;
+
+	do
+		c = lldbg_serial_in_poll();
+	while (c < 0);
+	return c;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/hexarg.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,29 @@
+/*
+ * Many commands take hex arguments.  This module contains the parse_hexarg()
+ * function, which is a wrapper around strtoul that performs some additional
+ * checks.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+lldbg_parse_hexarg(arg, maxdigits, valp)
+	char *arg;
+	int maxdigits;
+	u_long *valp;
+{
+	char *cp = arg, *bp;
+	int len;
+
+	if (cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X'))
+		cp += 2;
+	for (bp = cp; *cp; cp++)
+		if (!isxdigit(*cp))
+			return(-1);
+	len = cp - bp;
+	if (len < 1 || len > maxdigits)
+		return(-1);
+	*valp = strtoul(arg, 0, 16);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/main.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,11 @@
+#include "types.h"
+
+lldbg_main()
+{
+	lldbg_printf("\2\2\2*Low Level Debug mode entered\2");
+	for (;;) {
+		lldbg_putchar('>');
+		if (lldbg_command_entry())
+			lldbg_command_dispatch();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/ns16550.h	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,101 @@
+#ifndef __NS16550_H
+#define	__NS16550_H
+
+/* NS16550 registers */
+#define	NS16550_RBR	0
+#define	NS16550_THR	0
+#define	NS16550_IER	1
+#define	NS16550_IIR	2
+#define	NS16550_FCR	2
+#define	NS16550_LCR	3
+#define	NS16550_MCR	4
+#define	NS16550_LSR	5
+#define	NS16550_MSR	6
+#define	NS16550_SCR	7
+#define	NS16550_DLL	0
+#define	NS16550_DLM	1
+
+#ifndef __ASSEMBLER__
+#include "types.h"
+
+struct ns16550_regs {
+	u8	datareg;
+	u8	ier;
+	u8	iir_fcr;
+	u8	lcr;
+	u8	mcr;
+	u8	lsr;
+	u8	msr;
+	u8	scr;
+};
+#endif
+
+/* IER bits */
+#define	NS16550_IER_EDSSI	0x08
+#define	NS16550_IER_ELSI	0x04
+#define	NS16550_IER_ETBEI	0x02
+#define	NS16550_IER_ERBFI	0x01
+
+/* IIR bits */
+#define	NS16550_IIR_FIFOEN	0xC0
+#define	NS16550_IIR_INTID	0x0E
+#define	NS16550_IIR_INT_RLS	0x06
+#define	NS16550_IIR_INT_RDA	0x04
+#define	NS16550_IIR_INT_CTO	0x0C
+#define	NS16550_IIR_INT_THRE	0x02
+#define	NS16550_IIR_INT_MODEM	0x00
+#define	NS16550_IIR_INTPEND	0x01
+
+/* FCR bits */
+
+#define	NS16550_FCR_RXTR	0xC0
+#define	NS16550_FCR_RXTR_1	0x00
+#define	NS16550_FCR_RXTR_4	0x40
+#define	NS16550_FCR_RXTR_8	0x80
+#define	NS16550_FCR_RXTR_14	0xC0
+#define	NS16550_FCR_DMAMODE	0x08
+#define	NS16550_FCR_TXRST	0x04
+#define	NS16550_FCR_RXRST	0x02
+#define	NS16550_FCR_FIFOEN	0x01
+
+/* LCR bits */
+#define	NS16550_LCR_DLAB	0x80
+#define	NS16550_LCR_BREAK	0x40
+#define	NS16550_LCR_STICK	0x20
+#define	NS16550_LCR_EPS		0x10
+#define	NS16550_LCR_PEN		0x08
+#define	NS16550_LCR_STB		0x04
+#define	NS16550_LCR_WLS		0x03
+#define	NS16550_LCR_WLS_5	0x00
+#define	NS16550_LCR_WLS_6	0x01
+#define	NS16550_LCR_WLS_7	0x02
+#define	NS16550_LCR_WLS_8	0x03
+
+/* MCR bits */
+#define	NS16550_MCR_LOOP	0x10
+#define	NS16550_MCR_OUT2	0x08
+#define	NS16550_MCR_OUT1	0x04
+#define	NS16550_MCR_RTS		0x02
+#define	NS16550_MCR_DTR		0x01
+
+/* LSR bits */
+#define	NS16550_LSR_ERR		0x80
+#define	NS16550_LSR_TEMP	0x40
+#define	NS16550_LSR_THRE	0x20
+#define	NS16550_LSR_BI		0x10
+#define	NS16550_LSR_FE		0x08
+#define	NS16550_LSR_PE		0x04
+#define	NS16550_LSR_OE		0x02
+#define	NS16550_LSR_DR		0x01
+
+/* MSR bits */
+#define	NS16550_MSR_DCD		0x80
+#define	NS16550_MSR_RI		0x40
+#define	NS16550_MSR_DSR		0x20
+#define	NS16550_MSR_CTS		0x10
+#define	NS16550_MSR_DDCD	0x08
+#define	NS16550_MSR_TERI	0x04
+#define	NS16550_MSR_DDSR	0x02
+#define	NS16550_MSR_DCTS	0x01
+
+#endif	/* __NS16550_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/parseargs.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,39 @@
+/*
+ * This module contains the parse_args() function, which parses the "rest"
+ * part of an entered command into an argc/argv-style argument array.
+ */
+
+lldbg_parse_args(unparsed, minargs, maxargs, argv, argcp)
+	char *unparsed;
+	int minargs, maxargs;
+	char **argv;
+	int *argcp;
+{
+	int argc;
+	char *cp;
+
+	argc = 0;
+	for (cp = unparsed; ; ) {
+		while (*cp == ' ')
+			cp++;
+		if (!*cp)
+			break;
+		if (argc >= maxargs) {
+			lldbg_printf("ERROR: too many arguments\n");
+			return(-1);
+		}
+		argv[argc++] = cp;
+		while (*cp && *cp != ' ')
+			cp++;
+		if (*cp)
+			*cp++ = '\0';
+	}
+	if (argc < minargs) {
+		lldbg_printf("ERROR: too few arguments\n");
+		return(-1);
+	}
+	argv[argc] = 0;
+	if (argcp)
+		*argcp = argc;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/printf.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,13 @@
+#include <stdarg.h>
+
+int
+lldbg_printf(char *fmt, ...)
+{
+	va_list ap;
+	int len;
+
+	va_start(ap, fmt);
+	len = lldbg_doprnt(fmt, ap);
+	va_end(ap);
+	return(len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/putchar.c	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,9 @@
+extern void lldbg_serial_out();
+
+void
+lldbg_putchar(ch)
+{
+	if (ch == '\n')
+		lldbg_serial_out('\r');
+	lldbg_serial_out(ch);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/serio.S	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,34 @@
+#include "../include/config.h"
+#include "ns16550.h"
+
+/* this module implements the elementary serial I/O operations */
+
+	.text
+	.code	32
+	.globl	lldbg_serial_out
+lldbg_serial_out:
+	ldr	r2, =CONFIG_LLDBG_UART_BASE
+1:	ldrb	r3, [r2, #NS16550_LSR]
+	tst	r3, #NS16550_LSR_THRE
+	beq	1b
+	strb	r0, [r2, #NS16550_THR]
+	bx	lr
+
+	.globl	lldbg_serial_in_poll
+lldbg_serial_in_poll:
+	ldr	r2, =CONFIG_LLDBG_UART_BASE
+	ldrb	r3, [r2, #NS16550_LSR]
+	tst	r3, #NS16550_LSR_DR
+	ldrneb	r0, [r2, #NS16550_RBR]
+	mvneq	r0, #0
+	bx	lr
+
+#if 0	// not needed currently
+	.globl	lldbg_serial_flush
+lldbg_serial_flush:
+	ldr	r2, =CONFIG_LLDBG_UART_BASE
+1:	ldrb	r3, [r2, #NS16550_LSR]
+	tst	r3, #NS16550_LSR_TEMP
+	beq	1b
+	bx	lr
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gsm-fw/lldbg/types.h	Fri May 15 00:02:03 2015 +0000
@@ -0,0 +1,18 @@
+/*
+ * I personally like the u8/u16/u32 types, but I don't see them
+ * being defined in any of the headers provided by newlib.
+ * So we'll define them ourselves.
+ */
+
+#ifndef	__OUR_OWN_TYPES_H
+#define	__OUR_OWN_TYPES_H
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+typedef signed char s8;
+typedef signed short s16;
+typedef signed int s32;
+
+#endif	/* include guard */