Index: kernel/arch/ia64/src/drivers/ski.c
===================================================================
--- kernel/arch/ia64/src/drivers/ski.c	(revision eb2187c4b32d71314ef8e18b14cf7c74dd74473d)
+++ kernel/arch/ia64/src/drivers/ski.c	(revision 00eeffa5aa242b2af8d46a65e8f13cff1d01eb89)
@@ -60,8 +60,8 @@
 };
 
-static void ski_putuchar(outdev_t *, const char32_t);
+static void ski_write(outdev_t *, const char *, size_t);
 
 static outdev_operations_t skidev_ops = {
-	.write = ski_putuchar,
+	.write = ski_write,
 	.redraw = NULL,
 	.scroll_up = NULL,
@@ -182,5 +182,5 @@
 }
 
-static void ski_do_putchar(char ch)
+static void ski_do_putchar(uint8_t ch)
 {
 	asm volatile (
@@ -203,16 +203,18 @@
  *
  */
-static void ski_putuchar(outdev_t *dev, char32_t ch)
-{
+static void ski_write(outdev_t *dev, const char *s, size_t n)
+{
+	/* If the userspace owns the console, do not output anything. */
 	if (ski_parea.mapped && !console_override)
 		return;
 
-	if (ascii_check(ch)) {
-		if (ch == '\n')
+	const char *top = s + n;
+	assert(top >= s);
+
+	for (; s < top; s++) {
+		if (*s == '\n')
 			ski_do_putchar('\r');
 
-		ski_do_putchar(ch);
-	} else {
-		ski_do_putchar('?');
+		ski_do_putchar((uint8_t) *s);
 	}
 }
Index: kernel/arch/riscv64/src/drivers/ucb.c
===================================================================
--- kernel/arch/riscv64/src/drivers/ucb.c	(revision eb2187c4b32d71314ef8e18b14cf7c74dd74473d)
+++ kernel/arch/riscv64/src/drivers/ucb.c	(revision 00eeffa5aa242b2af8d46a65e8f13cff1d01eb89)
@@ -41,11 +41,4 @@
 static volatile uint64_t *fromhost;
 
-static outdev_operations_t htifdev_ops = {
-	.write = htif_putuchar,
-	.redraw = NULL,
-	.scroll_up = NULL,
-	.scroll_down = NULL
-};
-
 static void poll_fromhost()
 {
@@ -56,4 +49,36 @@
 	*fromhost = 0;
 }
+
+static void htif_cmd(uint8_t device, uint8_t cmd, uint64_t payload)
+{
+	uint64_t val = (((uint64_t) device) << 56) |
+	    (((uint64_t) cmd) << 48) |
+	    (payload & UINT64_C(0xffffffffffff));
+
+	while (*tohost)
+		poll_fromhost();
+
+	*tohost = val;
+}
+
+static void htif_write(outdev_t *dev, const char *s, size_t n)
+{
+	const char *top = s + n;
+	assert(top >= s);
+
+	for (; s < top; s++) {
+		if (*s == '\n')
+			htif_cmd(HTIF_DEVICE_CONSOLE, HTIF_CONSOLE_PUTC, '\r');
+
+		htif_cmd(HTIF_DEVICE_CONSOLE, HTIF_CONSOLE_PUTC, (uint8_t) *s);
+	}
+}
+
+static outdev_operations_t htifdev_ops = {
+	.write = htif_write,
+	.redraw = NULL,
+	.scroll_up = NULL,
+	.scroll_down = NULL
+};
 
 void htif_init(volatile uint64_t *tohost_addr, volatile uint64_t *fromhost_addr)
@@ -72,22 +97,2 @@
 	return htifdev;
 }
-
-static void htif_cmd(uint8_t device, uint8_t cmd, uint64_t payload)
-{
-	uint64_t val = (((uint64_t) device) << 56) |
-	    (((uint64_t) cmd) << 48) |
-	    (payload & UINT64_C(0xffffffffffff));
-
-	while (*tohost)
-		poll_fromhost();
-
-	*tohost = val;
-}
-
-void htif_putuchar(outdev_t *dev, const char32_t ch)
-{
-	if (ascii_check(ch))
-		htif_cmd(HTIF_DEVICE_CONSOLE, HTIF_CONSOLE_PUTC, ch);
-	else
-		htif_cmd(HTIF_DEVICE_CONSOLE, HTIF_CONSOLE_PUTC, U_SPECIAL);
-}
Index: kernel/arch/sparc64/src/drivers/niagara.c
===================================================================
--- kernel/arch/sparc64/src/drivers/niagara.c	(revision eb2187c4b32d71314ef8e18b14cf7c74dd74473d)
+++ kernel/arch/sparc64/src/drivers/niagara.c	(revision 00eeffa5aa242b2af8d46a65e8f13cff1d01eb89)
@@ -57,9 +57,9 @@
 static niagara_instance_t *instance = NULL;
 
-static void niagara_putuchar(outdev_t *, const char32_t);
+static void niagara_write(outdev_t *, const char *, size_t);
 
 /** Character device operations */
 static outdev_operations_t niagara_ops = {
-	.write = niagara_putuchar,
+	.write = niagara_write,
 	.redraw = NULL,
 	.scroll_up = NULL,
@@ -95,5 +95,5 @@
 
 /** Write a single character to the standard output. */
-static inline void do_putchar(char c)
+static inline void do_putchar(uint8_t c)
 {
 	/* Repeat until the buffer is non-full */
@@ -102,15 +102,18 @@
 }
 
-/** Write a single character to the standard output. */
-static void niagara_putuchar(outdev_t *dev, char32_t ch)
-{
-	if ((!outbuf_parea.mapped) || (console_override)) {
-		if (ascii_check(ch)) {
-			do_putchar(ch);
-			if (ch == '\n')
-				do_putchar('\r');
-		} else {
-			do_putchar('?');
-		}
+static void niagara_write(outdev_t *dev, const char *s, size_t n)
+{
+	/* If the userspace owns the console, do not output anything. */
+	if (outbuf_parea.mapped && !console_override)
+		return;
+
+	const char *top = s + n;
+	assert(top >= s);
+
+	for (; s < top; s++) {
+		if (*s == '\n')
+			do_putchar('\r');
+
+		do_putchar((uint8_t) *s);
 	}
 }
Index: kernel/genarch/src/drivers/dsrln/dsrlnout.c
===================================================================
--- kernel/genarch/src/drivers/dsrln/dsrlnout.c	(revision eb2187c4b32d71314ef8e18b14cf7c74dd74473d)
+++ kernel/genarch/src/drivers/dsrln/dsrlnout.c	(revision 00eeffa5aa242b2af8d46a65e8f13cff1d01eb89)
@@ -49,18 +49,24 @@
 } dsrlnout_instance_t;
 
-static void dsrlnout_putuchar(outdev_t *dev, const char32_t ch)
+static void dsrlnout_write(outdev_t *dev, const char *s, size_t n)
 {
 	dsrlnout_instance_t *instance = (dsrlnout_instance_t *) dev->data;
 
-	if ((!instance->parea.mapped) || (console_override)) {
-		if (ascii_check(ch))
-			pio_write_8(instance->base, ch);
-		else
-			pio_write_8(instance->base, U_SPECIAL);
+	if (instance->parea.mapped && !console_override)
+		return;
+
+	const char *top = s + n;
+	assert(top >= s);
+
+	for (; s < top; s++) {
+		if (*s == '\n')
+			pio_write_8(instance->base, '\r');
+
+		pio_write_8(instance->base, (uint8_t) *s);
 	}
 }
 
 static outdev_operations_t dsrlndev_ops = {
-	.write = dsrlnout_putuchar,
+	.write = dsrlnout_write,
 	.redraw = NULL,
 	.scroll_up = NULL,
Index: kernel/genarch/src/drivers/omap/uart.c
===================================================================
--- kernel/genarch/src/drivers/omap/uart.c	(revision eb2187c4b32d71314ef8e18b14cf7c74dd74473d)
+++ kernel/genarch/src/drivers/omap/uart.c	(revision 00eeffa5aa242b2af8d46a65e8f13cff1d01eb89)
@@ -49,18 +49,21 @@
 }
 
-static void omap_uart_putuchar(outdev_t *dev, char32_t ch)
+static void omap_uart_write(outdev_t *dev, const char *s, size_t n)
 {
 	omap_uart_t *uart = dev->data;
-	if (!ascii_check(ch)) {
-		omap_uart_txb(uart, U_SPECIAL);
-	} else {
-		if (ch == '\n')
+
+	const char *top = s + n;
+	assert(top >= s);
+
+	for (; s < top; s++) {
+		if (*s == '\n')
 			omap_uart_txb(uart, '\r');
-		omap_uart_txb(uart, ch);
+
+		omap_uart_txb(uart, (uint8_t) *s);
 	}
 }
 
 static outdev_operations_t omap_uart_ops = {
-	.write = omap_uart_putuchar,
+	.write = omap_uart_write,
 	.redraw = NULL,
 	.scroll_up = NULL,
Index: kernel/genarch/src/drivers/s3c24xx/uart.c
===================================================================
--- kernel/genarch/src/drivers/s3c24xx/uart.c	(revision eb2187c4b32d71314ef8e18b14cf7c74dd74473d)
+++ kernel/genarch/src/drivers/s3c24xx/uart.c	(revision 00eeffa5aa242b2af8d46a65e8f13cff1d01eb89)
@@ -49,9 +49,6 @@
 #include <str.h>
 
-static void s3c24xx_uart_sendb(outdev_t *dev, uint8_t byte)
+static void s3c24xx_uart_sendb(s3c24xx_uart_t *uart, uint8_t byte)
 {
-	s3c24xx_uart_t *uart =
-	    (s3c24xx_uart_t *) dev->data;
-
 	/* Wait for space becoming available in Tx FIFO. */
 	while ((pio_read_32(&uart->io->ufstat) & S3C24XX_UFSTAT_TX_FULL) != 0)
@@ -61,17 +58,20 @@
 }
 
-static void s3c24xx_uart_putuchar(outdev_t *dev, char32_t ch)
+static void s3c24xx_uart_write(outdev_t *dev, const char *s, size_t n)
 {
-	s3c24xx_uart_t *uart =
-	    (s3c24xx_uart_t *) dev->data;
+	s3c24xx_uart_t *uart = dev->data;
 
-	if ((!uart->parea.mapped) || (console_override)) {
-		if (!ascii_check(ch)) {
-			s3c24xx_uart_sendb(dev, U_SPECIAL);
-		} else {
-			if (ch == '\n')
-				s3c24xx_uart_sendb(dev, (uint8_t) '\r');
-			s3c24xx_uart_sendb(dev, (uint8_t) ch);
-		}
+	/* If the userspace owns the console, do not output anything. */
+	if (uart->parea.mapped && !console_override)
+		return;
+
+	const char *top = s + n;
+	assert(top >= s);
+
+	for (; s < top; s++) {
+		if (*s == '\n')
+			s3c24xx_uart_sendb(uart, '\r');
+
+		s3c24xx_uart_sendb(uart, (uint8_t) *s);
 	}
 }
@@ -94,5 +94,5 @@
 
 static outdev_operations_t s3c24xx_uart_ops = {
-	.write = s3c24xx_uart_putuchar,
+	.write = s3c24xx_uart_write,
 	.redraw = NULL,
 	.scroll_up = NULL,
