em100: Add SPI trace console

Add a command which allows a realtime console output from em100
when using the CONFIG_SPI_FLASH_CONSOLE option in coreboot.

Provide the offset and length of the region defined in the FMAP
and it will watch for the host to write to that area, and when
it sees a write it will print it to stdout.

Unlike the already implemented SPI terminal function, this does
not rely on the host sending EM100 specific commands (that might
be filtered out by the SPI controller or the ME)

Change-Id: I78162585bcd73a3bb3a8aa590f43a96092946109
Signed-off-by: Duncan Laurie <dlaurie@google.com>
Signed-off-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Reviewed-on: https://review.coreboot.org/c/em100/+/48599
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
diff --git a/em100.c b/em100.c
index 4b0a121..f808ed6 100644
--- a/em100.c
+++ b/em100.c
@@ -787,6 +787,8 @@
 	{"list-devices", 0, 0, 'l'},
 	{"update-files", 0, 0, 'U'},
 	{"terminal", 0, 0, 'T'},
+	{"traceconsole", 0, 0, 'R'},
+	{"length", 1, 0, 'L'},
 	{"compatible", 0, 0, 'C'},
 	{NULL, 0, 0, 0}
 };
@@ -807,6 +809,8 @@
 		"  -t|--trace:                     trace mode\n"
 		"  -O|--offset HEX_VAL:            address offset for trace mode\n"
 		"  -T|--terminal:                  terminal mode\n"
+		"  -R|--traceconsole:              trace console mode\n"
+		"  -L|--length HEX_VAL:            length of buffer for traceconsole mode\n"
 		"  -F|--firmware-update FILE|auto: update EM100pro firmware (dangerous)\n"
 		"  -f|--firmware-dump FILE:        export raw EM100pro firmware to file\n"
 		"  -g|--firmware-write FILE:       export EM100pro firmware to DPFW file\n"
@@ -832,18 +836,18 @@
 	const char *firmware_in = NULL, *firmware_out = NULL;
 	const char *holdpin = NULL;
 	int do_start = 0, do_stop = 0;
-	int verify = 0, trace = 0, terminal=0;
+	int verify = 0, trace = 0, terminal=0, traceconsole=0;
 	int compatibility = 0;
 	int bus = 0, device = 0;
 	int firmware_is_dpfw = 0;
 	unsigned int serial_number = 0;
-	unsigned long address_offset = 0;
+	unsigned long address_offset = 0, address_length = 0;
 	unsigned int spi_start_address = 0;
 	const char *voltage = NULL;
 	struct sigaction signal_action;
 	struct em100 *em100 = &em100_state;
 
-	while ((opt = getopt_long(argc, argv, "c:d:a:m:u:rsvtO:F:f:g:S:V:p:DCx:lUhT",
+	while ((opt = getopt_long(argc, argv, "c:d:a:m:u:rsvtO:F:f:g:S:V:p:DCx:lUhTRL:",
 				  longopts, &idx)) != -1) {
 		switch (opt) {
 		case 'c':
@@ -919,6 +923,12 @@
 		case 'U':
 			update_all_files();
 			return 0;
+		case 'R':
+			traceconsole = 1;
+			break;
+		case 'L':
+			sscanf(optarg, "%lx", &address_length);
+			break;
 		case 'C':
 			compatibility = 1;
 			break;
@@ -1206,7 +1216,7 @@
 		set_state(em100, 1);
 	}
 
-	if (trace || terminal) {
+	if (trace || terminal || traceconsole) {
 		int usb_errors = 0;
 
 		if ((holdpin == NULL) && (!set_hold_pin_state(em100, 3))) {
@@ -1220,7 +1230,7 @@
 
 		printf ("Starting ");
 
-		if (trace) {
+		if (trace || traceconsole) {
 			reset_spi_trace(em100);
 			printf("trace%s", terminal ? " & " : "");
 		}
@@ -1234,13 +1244,18 @@
 
 		while (!exit_requested && usb_errors < MAX_USB_ERRORS) {
 			int ret;
-			if (trace) {
+
+			if (traceconsole)
+				ret = read_spi_trace_console(em100, address_offset, address_length);
+			else if (trace)
 				ret = read_spi_trace(em100, terminal, address_offset);
-			} else {
+			else if (terminal)
 				ret = read_spi_terminal(em100, 0);
-			}
+
 			if (ret == 0)
 				usb_errors++;
+			if (ret < 0)
+				break;
 		}
 		if (usb_errors >= MAX_USB_ERRORS)
 			printf("Error: Bailed out with too many USB errors.\n");
diff --git a/em100.h b/em100.h
index ecb74d2..bf1bb87 100644
--- a/em100.h
+++ b/em100.h
@@ -172,6 +172,8 @@
 		   unsigned long addr_offset);
 int read_spi_terminal(struct em100 *em100, int print_counter);
 int init_spi_terminal(struct em100 *em100);
+int read_spi_trace_console(struct em100 *em100, unsigned long addr_offset,
+			   unsigned long addr_len);
 
 /* Archive handling */
 typedef struct {
diff --git a/trace.c b/trace.c
index 2afdbad..df3fdd9 100644
--- a/trace.c
+++ b/trace.c
@@ -330,6 +330,142 @@
 	return 1;
 }
 
+int read_spi_trace_console(struct em100 *em100, unsigned long addr_offset,
+			   unsigned long addr_len)
+{
+	unsigned char reportdata[REPORT_BUFFER_COUNT][REPORT_BUFFER_LENGTH] =
+			{{0}};
+	unsigned char *data;
+	unsigned int count, i, report;
+	static int additional_pad_bytes = 0;
+	static unsigned int address = 0;
+	static int do_write = 0;
+	static struct spi_cmd_values *spi_cmd_vals = &spi_command_list[3];
+	int addr_bytes = 0;
+
+	if (addr_offset == 0) {
+		printf("Address offset for console buffer required\n");
+		return -1;
+	}
+	if (addr_len == 0) {
+		printf("Console buffer length required\n");
+		return -1;
+	}
+
+	if (!read_report_buffer(em100, reportdata))
+		return -1;
+
+	for (report = 0; report < REPORT_BUFFER_COUNT; report++) {
+		data = &reportdata[report][0];
+		count = (data[0] << 8) | data[1];
+		if (!count)
+			continue;
+		if (count > 1023) {
+			printf("Warning: EM100pro sends too much data: %d.\n", count);
+			count = 1023;
+		}
+		for (i = 0; i < count; i++) {
+			unsigned int j = additional_pad_bytes;
+			unsigned int address_bytes = 0;
+			additional_pad_bytes = 0;
+			unsigned char cmd = data[2 + i*8];
+
+			/* from here, it must be data */
+			if (cmd != cmdid) {
+				unsigned char spi_command = data[i * 8 + 4];
+				spi_cmd_vals = get_command_vals(spi_command);
+
+				/* new command */
+				cmdid = cmd;
+
+				/* Special commands */
+				switch (spi_command) {
+				case 0xb7:
+					address_mode = 4;
+					break;
+				case 0xe9:
+					address_mode = 3;
+					break;
+				}
+
+				/* skip command byte */
+				j = 1;
+
+				/* Set up address if used by this command */
+				switch (spi_cmd_vals->address_type) {
+				case ADDR_DYNAMIC:
+					address_bytes = address_mode;
+					break;
+				case ADDR_NO_OFF_3B:
+				case ADDR_3B:
+					address_bytes = 3;
+					break;
+				case ADDR_4B:
+					address_bytes = 4;
+					break;
+				case ADDR_NONE:
+					break;
+				}
+
+				if (address_bytes == 3)
+					address = (data[i * 8 + 5] << 16)
+						+ (data[i * 8 + 6] << 8)
+						+ data[i * 8 + 7];
+				else if (address_bytes == 4)
+					address = (data[i * 8 + 5] << 24)
+						+ (data[i * 8 + 6] << 16)
+						+ (data[i * 8 + 7] << 8)
+						+ data[i * 8 + 8];
+
+				/* skip address bytes and padding */
+				j += address_bytes + spi_cmd_vals->pad_bytes;
+
+				if (j > MAX_TRACE_BLOCKLENGTH) {
+					additional_pad_bytes = j -
+						MAX_TRACE_BLOCKLENGTH;
+					j = MAX_TRACE_BLOCKLENGTH;
+				}
+
+#if 0
+				if (spi_cmd_vals->address_type != ADDR_NONE)
+					printf("0x%02x @ 0x%08x (%s)\n",
+					       spi_command, address, spi_cmd_vals->cmd_name);
+				else
+					printf("0x%02x (%s)\n",
+					       spi_command, spi_cmd_vals->cmd_name);
+#endif
+
+				curpos = 0;
+				if (spi_command == 0x02)
+					do_write = 1;
+				else
+					do_write = 0;
+			}
+
+			if (do_write == 0 ||
+			    (spi_cmd_vals->address_type == ADDR_NONE ||
+			     address < addr_offset ||
+			     address > (addr_offset + addr_len))) {
+				curpos = data[2 + i*8 + 1] + 0x10;
+				continue;
+			}
+
+			/* this exploits 8bit wrap around in curpos */
+			unsigned char blocklen = (data[2 + i*8 + 1] - curpos);
+			blocklen /= 8;
+
+			for (; j < blocklen; j++) {
+				printf("%c", data[i * 8 + addr_bytes + j]);
+			}
+
+			/* this is because the em100 counts funny */
+			curpos = data[2 + i*8 + 1] + 0x10;
+			fflush(stdout);
+		}
+	}
+	return 1;
+}
+
 #define UFIFO_SIZE	512
 #define UFIFO_TIMEOUT	0x00