lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1317061425-1030-1-git-send-email-dianders@chromium.org>
Date:	Mon, 26 Sep 2011 11:23:45 -0700
From:	Doug Anderson <dianders@...omium.org>
To:	Stephen Warren <swarren@...dia.com>,
	Olof Johansson <olof@...om.net>,
	Colin Cross <ccross@...roid.com>,
	Erik Gilling <konkers@...roid.com>
Cc:	Russell King <linux@....linux.org.uk>,
	Tony Lindgren <tony@...mide.com>,
	Lorenzo Pieralisi <lorenzo.pieralisi@....com>,
	Jeremy Kerr <jeremy.kerr@...onical.com>,
	Doug Anderson <dianders@...omium.org>,
	Simon Glass <sjg@...omium.org>,
	Allen Martin <AMartin@...dia.com>, linux-tegra@...r.kernel.org,
	linux-arm-kernel@...ts.infradead.org, linux-kernel@...r.kernel.org
Subject: [PATCH] ARM: tegra: Make earlyprintk choose a UART at runtime.

With this change we automatically detect which UART to use for
earlyprintk (and for printing during decompression).  The
detection involves coordination with the bootloader: it's expected
that the bootloader will leave a 'D' (for [D]ebug) in the UART
scratchpad register for whichever UART we should use for debugging.

If we don't find any UART that specified, we'll fall back to the
UART that was specified during config time:
CONFIG_TEGRA_DEBUG_UART_XXX.

As a side effect of this change, earlyprintk will no longer fail
if you've specified CONFIG_TEGRA_DEBUG_UART_NONE.

This change is in line with what is documented in the
Documentation/arm/Booting file.  In other words: it's expected
that the bootloader initialize one serial port so that the
kernel can detect it.

Other approaches considered:
* Hardcode based on machine ID (as many other ARM boards do).
  OK, but nice to not have yet another place to add per-board
  code.  Better to have bootloader parse device tree and pass us
  this info.
* Check for TXE bit (like SA1110).  Nice (and doesn't require
  a bootloader change), but a little less explicit.  Also: if
  bootloader (for some reason) uses another UART, it needs to
  remember to turn it off before jumping to the kernel or we may
  print to it.  NOTE: adapting this patch to check TXE is easy
  for me to move to if maintainers would prefer it.
* Check for which UART clock is enabled.  Similar to TXE
  solution, but might be more robust of reads from an unclocked
  UART hang the system.
* Communicate from decompression code to assembly with magic
  memory area (like OMAP_UART_INFO).  Would prefer to avoid for
  the downsides described in the OMAP patch that added this,
  especially the fact that running without a zImage would (I
  think) make earlyprintk fail.

Signed-off-by: Doug Anderson <dianders@...omium.org>
---
 arch/arm/mach-tegra/include/mach/debug-macro.S |  155 ++++++++++++++++++++++--
 arch/arm/mach-tegra/include/mach/uncompress.h  |   50 ++++++--
 2 files changed, 181 insertions(+), 24 deletions(-)

diff --git a/arch/arm/mach-tegra/include/mach/debug-macro.S b/arch/arm/mach-tegra/include/mach/debug-macro.S
index e0ebe65..8a4190a 100644
--- a/arch/arm/mach-tegra/include/mach/debug-macro.S
+++ b/arch/arm/mach-tegra/include/mach/debug-macro.S
@@ -1,11 +1,12 @@
 /*
  * arch/arm/mach-tegra/include/mach/debug-macro.S
  *
- * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 Google, Inc.
  *
  * Author:
  *	Colin Cross <ccross@...gle.com>
  *	Erik Gilling <konkers@...gle.com>
+ *	Doug Anderson <dianders@...omium.org>
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -18,18 +19,150 @@
  *
  */
 
+#include <linux/serial_reg.h>
+
+#include <asm/memory.h>
+
 #include <mach/io.h>
 #include <mach/iomap.h>
 
-	.macro  addruart, rp, rv
-        ldr     \rp, =IO_APB_PHYS       @ physical
-        ldr     \rv, =IO_APB_VIRT        @ virtual
-	orr	\rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF)
-	orr	\rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
-	orr	\rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF)
-	orr	\rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
-	.endm
+#define UART_SHIFT			2
+#define TEGRA_DEBUG_UART_OFFSET		(TEGRA_DEBUG_UART_BASE & 0xFFFF)
+
+#define tegra_uart_v2p(x)		((x) - PAGE_OFFSET + PLAT_PHYS_OFFSET)
+
+		/*
+		 * Order matters for this section; code below assumes that
+		 * phys is 4 from config and virt is 8 from config.
+		 *
+		 * By default, we'll start the UART by looking at whatever
+		 * was specified by CONFIG_TEGRA_DEBUG_UART_XXX, but on the
+		 * first use of addruart we'll search all UARTs for one with
+		 * a 'D' in the scratchpad register.  If we find one, we'll
+		 * use it instead.
+		 */
+		.pushsection .data
+tegra_uart_config:	.word	0
+tegra_uart_phys:	.word	IO_APB_PHYS + TEGRA_DEBUG_UART_OFFSET
+tegra_uart_virt:	.word	IO_APB_VIRT + TEGRA_DEBUG_UART_OFFSET
+		.popsection
+
+
+		/* Put address of tegra_uart_config into \ra */
+		.macro	get_uart_config_addr, ra
+		mrc	p15, 0, \ra, c1, c0
+		tst	\ra, #1					@ MMU enabled?
+		ldreq	\ra, =tegra_uart_v2p(tegra_uart_config)	@ not enabled
+		ldrne	\ra, =tegra_uart_config			@ enabled
+		.endm
+
+		/* Check whether a given UART is the debug UART; sets cond. */
+		.macro	check_uart, rio, rtmp, uart_base
+		orr	\rtmp, \rio, #(\uart_base & 0xFF)
+		orr	\rtmp, \rtmp, #(\uart_base & 0xFF00)
+		ldrb	\rtmp, [\rtmp, #(UART_SCR << UART_SHIFT)]
+		cmp	\rtmp, #'D'
+		.endm
+
+		/*
+		 * Store the given UART in the data section; needs two
+		 * temp registers to work with.
+		 */
+		.macro	store_uart, rx, ry, uart_base
+		get_uart_config_addr \rx
+		ldr	\ry, =(IO_APB_PHYS + (\uart_base & 0xFFFF))
+		str	\ry, [\rx, #4]
+		ldr	\ry, =(IO_APB_VIRT + (\uart_base & 0xFFFF))
+		str	\ry, [\rx, #8]
+		.endm
+
+
+		/*
+		 * Get phys and virt addr of the debug UART.  Return results in
+		 * registers \rp and \rv, which are the only two registers
+		 * we get to work with.  Code is loosely based on OMAP
+		 * version of debug-macro.S.
+		 */
+		.macro	addruart, rp, rv
+
+10:		get_uart_config_addr \rp
+		ldr	\rv, [\rp, #0]
+		cmp	\rv, #1			@ is port configured?
+		beq	99f			@ ...yes? jump to end
+
+		mov	\rv, #1 		@ store so next time we are
+		str	\rv, [\rp, #0]		@ consider oursevles configured
+
+		/* Use \rp to hold IO base address while we search for 'D' */
+		mrc	p15, 0, \rp, c1, c0
+		tst	\rp, #1			@ MMU enabled?
+		ldreq	\rp, =IO_APB_PHYS	@ MMU not enabled
+		ldrne	\rp, =IO_APB_VIRT	@ MMU enabled
+
+		/* Check for UARTA */
+		check_uart \rp, \rv, TEGRA_UARTA_BASE
+		bne	21f			@ not UARTA
+		store_uart \rp, \rv, TEGRA_UARTA_BASE
+		b	10b
+
+		/* Check for UARTB */
+21:		check_uart \rp, \rv, TEGRA_UARTB_BASE
+		bne	22f			@ not UARTB
+		store_uart \rp, \rv, TEGRA_UARTB_BASE
+		b	10b
+
+		/* Check for UARTC */
+22:		check_uart \rp, \rv, TEGRA_UARTC_BASE
+		bne	23f			@ not UARTC
+		store_uart \rp, \rv, TEGRA_UARTC_BASE
+		b	10b
+
+		/* Check for UARTD */
+23:		check_uart \rp, \rv, TEGRA_UARTD_BASE
+		bne	24f			@ not UARTD
+		store_uart \rp, \rv, TEGRA_UARTD_BASE
+		b	10b
+
+		/* Check for UARTE */
+24:		check_uart \rp, \rv, TEGRA_UARTE_BASE
+		bne	10b			@ not UARTE; give up
+		store_uart \rp, \rv, TEGRA_UARTE_BASE
+		b	10b
+
+		/* When you jump to here \rp has addr of tegra_uart_config */
+99:		ldr	\rv, [\rp, #8]		@ virt is 8 bytes after config
+		ldr	\rp, [\rp, #4]		@ phys is 4 bytes after config
+		.endm
+
+/*
+ * Code below is swiped from <asm/hardware/debug-8250.S>, but add an extra
+ * check to make sure that we aren't in the CONFIG_TEGRA_DEBUG_UART_NONE case.
+ * We use the fact that all 5 valid UARTS addresses all have something in the
+ * 2nd-to-lowest byte.
+ */
+		.macro	senduart,rd,rx
+		tst	\rx, #0x0000ff00
+		strneb	\rd, [\rx, #UART_TX << UART_SHIFT]
+1001:
+		.endm
 
-#define UART_SHIFT	2
-#include <asm/hardware/debug-8250.S>
+		.macro	busyuart,rd,rx
+		tst	\rx, #0x0000ff00
+		beq	1002f
+1001:		ldrb	\rd, [\rx, #UART_LSR << UART_SHIFT]
+		and	\rd, \rd, #UART_LSR_TEMT | UART_LSR_THRE
+		teq	\rd, #UART_LSR_TEMT | UART_LSR_THRE
+		bne	1001b
+1002:
+		.endm
 
+		.macro	waituart,rd,rx
+#ifdef FLOW_CONTROL
+		tst	\rx, #0x0000ff00
+		beq	1002f
+1001:		ldrb	\rd, [\rx, #UART_MSR << UART_SHIFT]
+		tst	\rd, #UART_MSR_CTS
+		beq	1001b
+1002:
+#endif
+		.endm
diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h
index 4e83237..2f776bd 100644
--- a/arch/arm/mach-tegra/include/mach/uncompress.h
+++ b/arch/arm/mach-tegra/include/mach/uncompress.h
@@ -1,11 +1,12 @@
 /*
  * arch/arm/mach-tegra/include/mach/uncompress.h
  *
- * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2011 Google, Inc.
  *
  * Author:
  *	Colin Cross <ccross@...gle.com>
  *	Erik Gilling <konkers@...gle.com>
+ *	Doug Anderson <dianders@...omium.org>
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -21,40 +22,63 @@
 #ifndef __MACH_TEGRA_UNCOMPRESS_H
 #define __MACH_TEGRA_UNCOMPRESS_H
 
+#include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/serial_reg.h>
 
 #include <mach/iomap.h>
 
+u32 uart_base;
+
+#define DEBUG_UART_SHIFT	2
+
 static void putc(int c)
 {
-	volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
-	int shift = 2;
+	volatile u8 *uart = (volatile u8 *)uart_base;
 
 	if (uart == NULL)
 		return;
 
-	while (!(uart[UART_LSR << shift] & UART_LSR_THRE))
+	while (!(uart[UART_LSR << DEBUG_UART_SHIFT] & UART_LSR_THRE))
 		barrier();
-	uart[UART_TX << shift] = c;
+	uart[UART_TX << DEBUG_UART_SHIFT] = c;
 }
 
 static inline void flush(void)
 {
 }
 
+/*
+ * Setup before decompression.  This is where we do UART selection for
+ * earlyprintk and init the uart_base register.
+ */
 static inline void arch_decomp_setup(void)
 {
-	volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
-	int shift = 2;
+	static const u32 uarts[] = {
+		TEGRA_UARTA_BASE,
+		TEGRA_UARTB_BASE,
+		TEGRA_UARTC_BASE,
+		TEGRA_UARTD_BASE,
+		TEGRA_UARTE_BASE,
+	};
+	const u32 num_uarts = ARRAY_SIZE(uarts);
+	u32 i;
 
-	if (uart == NULL)
-		return;
+	/*
+	 * Look for the first UART that has a 'D' in the scratchpad register,
+	 * which should be set by the bootloader to tell us which UART to use
+	 * for debugging.  If nothing found, we'll fall back to what's
+	 * specified in TEGRA_DEBUG_UART_BASE.
+	 */
+	uart_base = TEGRA_DEBUG_UART_BASE;
+	for (i = 0; i < num_uarts; i++) {
+		volatile u8 *uart = (volatile u8 *)uarts[i];
 
-	uart[UART_LCR << shift] |= UART_LCR_DLAB;
-	uart[UART_DLL << shift] = 0x75;
-	uart[UART_DLM << shift] = 0x0;
-	uart[UART_LCR << shift] = 3;
+		if (uart[UART_SCR << DEBUG_UART_SHIFT] == 'D') {
+			uart_base = uarts[i];
+			break;
+		}
+	}
 }
 
 static inline void arch_decomp_wdog(void)
-- 
1.7.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ