sparc: Write to prom console using indirect buffer.
authorDavid S. Miller <davem@davemloft.net>
Wed, 1 Dec 2010 04:15:58 +0000 (20:15 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 1 Dec 2010 04:15:58 +0000 (20:15 -0800)
sparc64 systems have a restriction in that passing in buffer
addressses above 4GB to prom calls is not reliable.

We end up violating this when we do prom console writes, because we
use an on-stack buffer to translate '\n' into '\r\n'.

So instead, do this translation into an intermediate buffer, which is
in the kernel image and thus below 4GB, then pass that to the PROM
console write calls.

On the 32-bit side we don't have to deal with any of these issues, so
the new prom_console_write_buf() uses the existing prom_nbputchar()
implementation.  However we can now mark those routines static.

Since the 64-bit side completely uses new code we can delete the
putchar bits as they are now completely unused.

Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc/include/asm/oplib_32.h
arch/sparc/include/asm/oplib_64.h
arch/sparc/prom/console_32.c
arch/sparc/prom/console_64.c
arch/sparc/prom/printf.c

index 40bc9efb1ac42d27e1f6dba7fe3a7263c3d0626c..9e5c64084b865781ca6782ced49b670ae35a5490 100644 (file)
@@ -102,8 +102,8 @@ extern int prom_getrev(void);
 /* Get the prom firmware revision. */
 extern int prom_getprev(void);
 
-/* Blocking put character to console. */
-extern void prom_putchar(const char *buf);
+/* Write a buffer of characters to the console. */
+extern void prom_console_write_buf(const char *buf, int len);
 
 /* Prom's internal routines, don't use in kernel/boot code. */
 extern void prom_printf(const char *fmt, ...);
index d4613738c537fd8051c7793bec076ef0b908c138..8cd0df34e82b86ada58f1399a8dab12a260adacb 100644 (file)
@@ -94,8 +94,8 @@ extern void prom_halt_power_off(void) __attribute__ ((noreturn));
  */
 extern unsigned char prom_get_idprom(char *idp_buffer, int idpbuf_size);
 
-/* Blocking put character to console. */
-extern void prom_putchar(const char *buf);
+/* Write a buffer of characters to the console. */
+extern void prom_console_write_buf(const char *buf, int len);
 
 /* Prom's internal routines, don't use in kernel/boot code. */
 extern void prom_printf(const char *fmt, ...);
index 157019e29fd4d950cc494580b6bfbcd730a5090d..48863108a44ce6af379436c7f32ba2063e39a3f2 100644 (file)
@@ -43,12 +43,14 @@ static int prom_nbputchar(const char *buf)
        return i; /* Ugh, we could spin forever on unsupported proms ;( */
 }
 
-/* Blocking version of put character routine above. */
-void prom_putchar(const char *buf)
+void prom_console_write_buf(const char *buf, int len)
 {
-       while (1) {
-               int err = prom_nbputchar(buf);
-               if (!err)
-                       break;
+       while (len) {
+               int n = prom_nbputchar(buf);
+               if (n)
+                       continue;
+               len--;
+               buf++;
        }
 }
+
index 0da88d10beffe1b3626f1998bb09798ef0d1724e..ed39e75828bd1eb6057f192d052ab88f5b943ddd 100644 (file)
 
 extern int prom_stdin, prom_stdout;
 
-/* Non blocking put character to console device, returns -1 if
- * unsuccessful.
- */
-static int prom_nbputchar(const char *buf)
+static int __prom_console_write_buf(const char *buf, int len)
 {
        unsigned long args[7];
+       int ret;
 
        args[0] = (unsigned long) "write";
        args[1] = 3;
        args[2] = 1;
        args[3] = (unsigned int) prom_stdout;
        args[4] = (unsigned long) buf;
-       args[5] = 1;
+       args[5] = (unsigned int) len;
        args[6] = (unsigned long) -1;
 
        p1275_cmd_direct(args);
 
-       if (args[6] == 1)
-               return 0;
-       else
+       ret = (int) args[6];
+       if (ret < 0)
                return -1;
+       return ret;
 }
 
-/* Blocking version of put character routine above. */
-void prom_putchar(const char *buf)
+void prom_console_write_buf(const char *buf, int len)
 {
-       while (1) {
-               int err = prom_nbputchar(buf);
-               if (!err)
-                       break;
+       while (len) {
+               int n = __prom_console_write_buf(buf, len);
+               if (n < 0)
+                       continue;
+               len -= n;
+               buf += len;
        }
 }
index 24031971f8067b9c9770f34b8f682d62c54b865c..d9682f06b3b056c7862ec7cda4214a95ab213a03 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/compiler.h>
+#include <linux/spinlock.h>
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
+#define CONSOLE_WRITE_BUF_SIZE 1024
+
 static char ppbuf[1024];
+static char console_write_buf[CONSOLE_WRITE_BUF_SIZE];
+static DEFINE_RAW_SPINLOCK(console_write_lock);
 
 void notrace prom_write(const char *buf, unsigned int n)
 {
+       unsigned int dest_len;
+       unsigned long flags;
+       char *dest;
+
+       dest = console_write_buf;
+       raw_spin_lock_irqsave(&console_write_lock, flags);
+
+       dest_len = 0;
        while (n-- != 0) {
-               char ch = *buf;
+               char ch = *buf++;
                if (ch == '\n') {
-                       char tmp = '\r';
-                       prom_putchar(&tmp);
+                       *dest++ = '\r';
+                       dest_len++;
+               }
+               *dest++ = ch;
+               dest_len++;
+               if (dest_len >= CONSOLE_WRITE_BUF_SIZE - 1) {
+                       prom_console_write_buf(console_write_buf, dest_len);
+                       dest = console_write_buf;
+                       dest_len = 0;
                }
-               prom_putchar(buf);
-               buf++;
        }
+       if (dest_len)
+               prom_console_write_buf(console_write_buf, dest_len);
+
+       raw_spin_unlock_irqrestore(&console_write_lock, flags);
 }
 
 void notrace prom_printf(const char *fmt, ...)