Index: uspace/app/terminal/terminal.c
===================================================================
--- uspace/app/terminal/terminal.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/app/terminal/terminal.c	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -37,4 +37,5 @@
 #include <adt/list.h>
 #include <adt/prodcons.h>
+#include <as.h>
 #include <errno.h>
 #include <fbfont/font-8x16.h>
@@ -49,4 +50,5 @@
 #include <task.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <str.h>
@@ -86,4 +88,8 @@
 static void term_set_cursor_visibility(con_srv_t *, bool);
 static errno_t term_get_event(con_srv_t *, cons_event_t *);
+static errno_t term_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
+static void term_unmap(con_srv_t *);
+static void term_buf_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t);
 
 static con_ops_t con_ops = {
@@ -102,5 +108,8 @@
 	.set_rgb_color = term_set_rgb_color,
 	.set_cursor_visibility = term_set_cursor_visibility,
-	.get_event = term_get_event
+	.get_event = term_get_event,
+	.map = term_map,
+	.unmap = term_unmap,
+	.update = term_buf_update
 };
 
@@ -344,5 +353,5 @@
 				charfield_t *back_field =
 				    chargrid_charfield_at(term->backbuf, x, y);
-				bool update = false;
+				bool cupdate = false;
 
 				if ((front_field->flags & CHAR_FLAG_DIRTY) ==
@@ -350,5 +359,5 @@
 					if (front_field->ch != back_field->ch) {
 						back_field->ch = front_field->ch;
-						update = true;
+						cupdate = true;
 					}
 
@@ -356,5 +365,5 @@
 					    back_field->attrs)) {
 						back_field->attrs = front_field->attrs;
-						update = true;
+						cupdate = true;
 					}
 
@@ -362,5 +371,5 @@
 				}
 
-				if (update) {
+				if (cupdate) {
 					term_update_char(term, &pixelmap, sx, sy, x, y);
 					update = true;
@@ -645,4 +654,119 @@
 }
 
+/** Create shared buffer for efficient rendering.
+ *
+ * @param srv Console server
+ * @param cols Number of columns in buffer
+ * @param rows Number of rows in buffer
+ * @param rbuf Place to store pointer to new sharable buffer
+ *
+ * @return EOK on sucess or an error code
+ */
+static errno_t term_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
+    charfield_t **rbuf)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	void *buf;
+
+	fibril_mutex_lock(&term->mtx);
+
+	if (term->ubuf != NULL) {
+		fibril_mutex_unlock(&term->mtx);
+		return EBUSY;
+	}
+
+	buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
+	    AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
+	if (buf == AS_MAP_FAILED) {
+		fibril_mutex_unlock(&term->mtx);
+		return ENOMEM;
+	}
+
+	term->ucols = cols;
+	term->urows = rows;
+	term->ubuf = buf;
+	fibril_mutex_unlock(&term->mtx);
+
+	*rbuf = buf;
+	return EOK;
+}
+
+/** Delete shared buffer.
+ *
+ * @param srv Console server
+ */
+static void term_unmap(con_srv_t *srv)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	void *buf;
+
+	fibril_mutex_lock(&term->mtx);
+
+	buf = term->ubuf;
+	term->ubuf = NULL;
+
+	if (buf != NULL)
+		as_area_destroy(buf);
+
+	fibril_mutex_unlock(&term->mtx);
+}
+
+/** Update area of terminal from shared buffer.
+ *
+ * @param srv Console server
+ * @param c0 Column coordinate of top-left corner (inclusive)
+ * @param r0 Row coordinate of top-left corner (inclusive)
+ * @param c1 Column coordinate of bottom-right corner (exclusive)
+ * @param r1 Row coordinate of bottom-right corner (exclusive)
+ */
+static void term_buf_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
+    sysarg_t c1, sysarg_t r1)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	charfield_t *ch;
+	sysarg_t col, row;
+
+	fibril_mutex_lock(&term->mtx);
+
+	if (term->ubuf == NULL) {
+		fibril_mutex_unlock(&term->mtx);
+		return;
+	}
+
+	/* Make sure we have meaningful coordinates, within bounds */
+
+	if (c1 > term->ucols)
+		c1 = term->ucols;
+	if (c1 > term->cols)
+		c1 = term->cols;
+	if (c0 >= c1) {
+		fibril_mutex_unlock(&term->mtx);
+		return;
+	}
+	if (r1 > term->urows)
+		r1 = term->urows;
+	if (r1 > term->rows)
+		r1 = term->rows;
+	if (r0 >= r1) {
+		fibril_mutex_unlock(&term->mtx);
+		return;
+	}
+
+	/* Update front buffer from user buffer */
+
+	for (row = r0; row < r1; row++) {
+		for (col = c0; col < c1; col++) {
+			ch = chargrid_charfield_at(term->frontbuf, col, row);
+			*ch = term->ubuf[row * term->ucols + col];
+		}
+	}
+
+	fibril_mutex_unlock(&term->mtx);
+
+	/* Update terminal */
+	term_update(term);
+	gfx_update(term->gc);
+}
+
 static void deinit_terminal(terminal_t *term)
 {
Index: uspace/app/terminal/terminal.h
===================================================================
--- uspace/app/terminal/terminal.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/app/terminal/terminal.h	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2012 Petr Koupy
  * All rights reserved.
@@ -85,4 +85,8 @@
 	sysarg_t top_row;
 
+	sysarg_t ucols;
+	sysarg_t urows;
+	charfield_t *ubuf;
+
 	service_id_t dsid;
 	con_srvs_t srvs;
Index: uspace/lib/c/generic/io/con_srv.c
===================================================================
--- uspace/lib/c/generic/io/con_srv.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/lib/c/generic/io/con_srv.c	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,4 +34,5 @@
  * @brief Console protocol server stub
  */
+#include <as.h>
 #include <errno.h>
 #include <io/cons_event.h>
@@ -307,4 +308,82 @@
 }
 
+/** Create shared buffer for efficient rendering */
+static void con_map_srv(con_srv_t *srv, ipc_call_t *icall)
+{
+	errno_t rc;
+	charfield_t *buf;
+	sysarg_t cols, rows;
+	ipc_call_t call;
+	size_t size;
+
+	if (srv->srvs->ops->map == NULL || srv->srvs->ops->unmap == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	cols = ipc_get_arg1(icall);
+	rows = ipc_get_arg2(icall);
+
+	if (!async_share_in_receive(&call, &size)) {
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	/* Check size */
+	if (size != PAGES2SIZE(SIZE2PAGES(cols * rows * sizeof(charfield_t)))) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = srv->srvs->ops->map(srv, cols, rows, &buf);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = async_share_in_finalize(&call, buf, AS_AREA_READ |
+	    AS_AREA_WRITE | AS_AREA_CACHEABLE);
+	if (rc != EOK) {
+		srv->srvs->ops->unmap(srv);
+		async_answer_0(icall, EIO);
+		return;
+	}
+
+	async_answer_0(icall, EOK);
+}
+
+/** Delete shared buffer */
+static void con_unmap_srv(con_srv_t *srv, ipc_call_t *icall)
+{
+	if (srv->srvs->ops->unmap == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->unmap(srv);
+	async_answer_0(icall, EOK);
+}
+
+/** Update console area from shared buffer */
+static void con_update_srv(con_srv_t *srv, ipc_call_t *icall)
+{
+	sysarg_t c0, r0, c1, r1;
+
+	c0 = ipc_get_arg1(icall);
+	r0 = ipc_get_arg2(icall);
+	c1 = ipc_get_arg3(icall);
+	r1 = ipc_get_arg4(icall);
+
+	if (srv->srvs->ops->update == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->update(srv, c0, r0, c1, r1);
+	async_answer_0(icall, EOK);
+}
+
 static con_srv_t *con_srv_create(con_srvs_t *srvs)
 {
@@ -412,4 +491,13 @@
 			con_get_event_srv(srv, &call);
 			break;
+		case CONSOLE_MAP:
+			con_map_srv(srv, &call);
+			break;
+		case CONSOLE_UNMAP:
+			con_unmap_srv(srv, &call);
+			break;
+		case CONSOLE_UPDATE:
+			con_update_srv(srv, &call);
+			break;
 		default:
 			async_answer_0(&call, ENOTSUP);
Index: uspace/lib/c/generic/io/console.c
===================================================================
--- uspace/lib/c/generic/io/console.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/lib/c/generic/io/console.c	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,6 +1,6 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2006 Josef Cejka
  * Copyright (c) 2006 Jakub Vana
- * Copyright (c) 2008 Jiri Svoboda
  * All rights reserved.
  *
@@ -35,4 +35,5 @@
  */
 
+#include <as.h>
 #include <libc.h>
 #include <async.h>
@@ -264,4 +265,80 @@
 }
 
+/** Create a shared buffer for fast rendering to the console.
+ *
+ * @param ctrl Console
+ * @param cols Number of columns
+ * @param rows Number of rows
+ * @param rbuf Place to store pointer to the shared buffer
+ * @return EOK on success or an error code
+ */
+errno_t console_map(console_ctrl_t *ctrl, sysarg_t cols, sysarg_t rows,
+    charfield_t **rbuf)
+{
+	async_exch_t *exch = NULL;
+	void *buf;
+	aid_t req;
+	ipc_call_t answer;
+	size_t asize;
+	errno_t rc;
+
+	exch = async_exchange_begin(ctrl->output_sess);
+	req = async_send_2(exch, CONSOLE_MAP, cols, rows, &answer);
+	if (rc != EOK)
+		goto error;
+
+	asize = PAGES2SIZE(SIZE2PAGES(cols * rows * sizeof(charfield_t)));
+
+	rc = async_share_in_start_0_0(exch, asize, &buf);
+	if (rc != EOK) {
+		async_forget(req);
+		goto error;
+	}
+
+	async_exchange_end(exch);
+	exch = NULL;
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	*rbuf = (charfield_t *)buf;
+	return EOK;
+error:
+	if (exch != NULL)
+		async_exchange_end(exch);
+	return rc;
+}
+
+/** Unmap console shared buffer.
+ *
+ * @param ctrl Console
+ * @param buf Buffer
+ */
+void console_unmap(console_ctrl_t *ctrl, charfield_t *buf)
+{
+	as_area_destroy(buf);
+}
+
+/** Update console rectangle from shared buffer.
+ *
+ * @param ctrl Console
+ * @param c0 Column coordinate of top-left corner (inclusive)
+ * @param r0 Row coordinate of top-left corner (inclusive)
+ * @param c1 Column coordinate of bottom-right corner (exclusive)
+ * @param r1 Row coordinate of bottom-right corner (exclusive)
+ *
+ * @return EOK on sucess or an error code
+ */
+errno_t console_update(console_ctrl_t *ctrl, sysarg_t c0, sysarg_t r0,
+    sysarg_t c1, sysarg_t r1)
+{
+	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
+	errno_t rc = async_req_4_0(exch, CONSOLE_UPDATE, c0, r0, c1, r1);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/io/con_srv.h
===================================================================
--- uspace/lib/c/include/io/con_srv.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/lib/c/include/io/con_srv.h	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 #include <async.h>
 #include <fibril_synch.h>
+#include <io/charfield.h>
 #include <io/color.h>
 #include <io/concaps.h>
@@ -83,4 +84,7 @@
 	void (*set_cursor_visibility)(con_srv_t *, bool);
 	errno_t (*get_event)(con_srv_t *, cons_event_t *);
+	errno_t (*map)(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
+	void (*unmap)(con_srv_t *);
+	void (*update)(con_srv_t *, sysarg_t, sysarg_t, sysarg_t, sysarg_t);
 };
 
Index: uspace/lib/c/include/io/console.h
===================================================================
--- uspace/lib/c/include/io/console.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/lib/c/include/io/console.h	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2008 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,4 +37,5 @@
 
 #include <time.h>
+#include <io/charfield.h>
 #include <io/concaps.h>
 #include <io/kbd_event.h>
@@ -86,4 +87,9 @@
 extern bool console_get_event_timeout(console_ctrl_t *, cons_event_t *,
     usec_t *);
+extern errno_t console_map(console_ctrl_t *, sysarg_t, sysarg_t,
+    charfield_t **);
+extern void console_unmap(console_ctrl_t *, charfield_t *);
+extern errno_t console_update(console_ctrl_t *, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t);
 
 #endif
Index: uspace/lib/c/include/ipc/console.h
===================================================================
--- uspace/lib/c/include/ipc/console.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/lib/c/include/ipc/console.h	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2006 Josef Cejka
  * All rights reserved.
@@ -48,5 +49,8 @@
 	CONSOLE_SET_COLOR,
 	CONSOLE_SET_RGB_COLOR,
-	CONSOLE_SET_CURSOR_VISIBILITY
+	CONSOLE_SET_CURSOR_VISIBILITY,
+	CONSOLE_MAP,
+	CONSOLE_UNMAP,
+	CONSOLE_UPDATE
 } console_request_t;
 
Index: uspace/lib/congfx/private/console.h
===================================================================
--- uspace/lib/congfx/private/console.h	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/lib/congfx/private/console.h	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 
 #include <gfx/context.h>
+#include <io/charfield.h>
 #include <io/console.h>
 #include <io/pixel.h>
@@ -58,4 +59,6 @@
 	/** Current drawing color */
 	pixel_t clr;
+	/** Shared console buffer */
+	charfield_t *buf;
 };
 
Index: uspace/lib/congfx/src/console.c
===================================================================
--- uspace/lib/congfx/src/console.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/lib/congfx/src/console.c	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -92,22 +92,28 @@
 {
 	console_gc_t *cgc = (console_gc_t *) arg;
-	int rv;
 	gfx_coord_t x, y;
-
-	// XXX We should handle p0.x > p1.x and p0.y > p1.y
-
-	console_set_rgb_color(cgc->con, cgc->clr, cgc->clr);
-
-	for (y = rect->p0.y; y < rect->p1.y; y++) {
-		console_set_pos(cgc->con, rect->p0.x, y);
-
-		for (x = rect->p0.x; x < rect->p1.x; x++) {
-			rv = fputc('X', cgc->fout);
-			if (rv < 0)
-				return EIO;
-		}
-
-		console_flush(cgc->con);
-	}
+	gfx_coord_t cols;
+	gfx_rect_t crect;
+	charfield_t ch;
+
+	/* Make sure rectangle is clipped and sorted */
+	gfx_rect_clip(rect, &cgc->rect, &crect);
+
+	cols = cgc->rect.p1.x - cgc->rect.p0.x;
+
+	ch.ch = 0;
+	ch.flags = CHAR_FLAG_DIRTY;
+	ch.attrs.type = CHAR_ATTR_RGB;
+	ch.attrs.val.rgb.fgcolor = cgc->clr;
+	ch.attrs.val.rgb.bgcolor = cgc->clr;
+
+	for (y = crect.p0.y; y < crect.p1.y; y++) {
+		for (x = crect.p0.x; x < crect.p1.x; x++) {
+			cgc->buf[y * cols + x] = ch;
+		}
+	}
+
+	console_update(cgc->con, crect.p0.x, crect.p0.y,
+	    crect.p1.x, crect.p1.y);
 
 	return EOK;
@@ -131,4 +137,5 @@
 	sysarg_t rows;
 	sysarg_t cols;
+	charfield_t *buf;
 	errno_t rc;
 
@@ -140,4 +147,8 @@
 
 	rc = console_get_size(con, &cols, &rows);
+	if (rc != EOK)
+		goto error;
+
+	rc = console_map(con, cols, rows, &buf);
 	if (rc != EOK)
 		goto error;
@@ -154,4 +165,5 @@
 	cgc->rect.p1.x = cols;
 	cgc->rect.p1.y = rows - 1; /* make sure we avoid bottom-right corner */
+	cgc->buf = buf;
 
 	*rgc = cgc;
@@ -268,6 +280,6 @@
 	console_gc_bitmap_t *cbm = (console_gc_bitmap_t *)bm;
 	gfx_coord_t x, y;
-	int rv;
 	pixel_t clr;
+	charfield_t ch;
 	pixelmap_t pixelmap;
 	gfx_rect_t srect;
@@ -275,4 +287,5 @@
 	gfx_rect_t crect;
 	gfx_coord2_t offs;
+	gfx_coord_t cols;
 
 	if (srect0 != NULL)
@@ -295,4 +308,6 @@
 	pixelmap.data = cbm->alloc.pixels;
 
+	cols = cbm->cgc->rect.p1.x - cbm->cgc->rect.p0.x;
+
 	if ((cbm->flags & bmpf_color_key) == 0) {
 		/* Simple copy */
@@ -304,11 +319,12 @@
 				    x - offs.x - cbm->rect.p0.x,
 				    y - offs.y - cbm->rect.p0.y);
-				console_set_rgb_color(cbm->cgc->con, clr, clr);
-
-				rv = fputc('X', cbm->cgc->fout);
-				if (rv < 0)
-					return EIO;
-
-				console_flush(cbm->cgc->con);
+
+				ch.ch = 0;
+				ch.flags = CHAR_FLAG_DIRTY;
+				ch.attrs.type = CHAR_ATTR_RGB;
+				ch.attrs.val.rgb.fgcolor = clr;
+				ch.attrs.val.rgb.bgcolor = clr;
+
+				cbm->cgc->buf[y * cols + x] = ch;
 			}
 		}
@@ -321,15 +337,13 @@
 				    x - offs.x - cbm->rect.p0.x,
 				    y - offs.y - cbm->rect.p0.y);
-				console_set_rgb_color(cbm->cgc->con, clr, clr);
-
-				if (clr != cbm->key_color) {
-					console_set_pos(cbm->cgc->con, x, y);
-					rv = fputc('X', cbm->cgc->fout);
-					if (rv < 0)
-						return EIO;
-
-					console_flush(cbm->cgc->con);
-				}
-
+
+				ch.ch = 0;
+				ch.flags = CHAR_FLAG_DIRTY;
+				ch.attrs.type = CHAR_ATTR_RGB;
+				ch.attrs.val.rgb.fgcolor = clr;
+				ch.attrs.val.rgb.bgcolor = clr;
+
+				if (clr != cbm->key_color)
+					cbm->cgc->buf[y * cols + x] = ch;
 			}
 		}
@@ -338,24 +352,24 @@
 		console_set_rgb_color(cbm->cgc->con, cbm->cgc->clr,
 		    cbm->cgc->clr);
+		ch.ch = 0;
+		ch.flags = CHAR_FLAG_DIRTY;
+		ch.attrs.type = CHAR_ATTR_RGB;
+		ch.attrs.val.rgb.fgcolor = cbm->cgc->clr;
+		ch.attrs.val.rgb.bgcolor = cbm->cgc->clr;
 
 		for (y = crect.p0.y; y < crect.p1.y; y++) {
 			for (x = crect.p0.x; x < crect.p1.x; x++) {
-
 				clr = pixelmap_get_pixel(&pixelmap,
 				    x - offs.x - cbm->rect.p0.x,
 				    y - offs.y - cbm->rect.p0.y);
 
-				if (clr != cbm->key_color) {
-					console_set_pos(cbm->cgc->con, x, y);
-					rv = fputc('X', cbm->cgc->fout);
-					if (rv < 0)
-						return EIO;
-
-					console_flush(cbm->cgc->con);
-				}
-
+				if (clr != cbm->key_color)
+					cbm->cgc->buf[y * cols + x] = ch;
 			}
 		}
 	}
+
+	console_update(cbm->cgc->con, crect.p0.x, crect.p0.y, crect.p1.x,
+	    crect.p1.y);
 
 	return EOK;
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision 2ab8ab3c0a1e9771074c6e9d07ec1bf5e87ddafc)
+++ uspace/srv/hid/console/console.c	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2011 Martin Decky
  * All rights reserved.
@@ -79,4 +80,8 @@
 	console_caps_t ccaps;  /**< Console capabilities */
 
+	sysarg_t ucols;		/**< Number of columns in user buffer */
+	sysarg_t urows;		/**< Number of rows in user buffer */
+	charfield_t *ubuf;	/**< User buffer */
+
 	chargrid_t *frontbuf;    /**< Front buffer */
 	frontbuf_handle_t fbid;  /**< Front buffer handle */
@@ -135,4 +140,8 @@
 static void cons_set_cursor_visibility(con_srv_t *, bool);
 static errno_t cons_get_event(con_srv_t *, cons_event_t *);
+static errno_t cons_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
+static void cons_unmap(con_srv_t *);
+static void cons_buf_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t);
 
 static con_ops_t con_ops = {
@@ -151,5 +160,8 @@
 	.set_rgb_color = cons_set_rgb_color,
 	.set_cursor_visibility = cons_set_cursor_visibility,
-	.get_event = cons_get_event
+	.get_event = cons_get_event,
+	.map = cons_map,
+	.unmap = cons_unmap,
+	.update = cons_buf_update
 };
 
@@ -508,4 +520,118 @@
 }
 
+/** Create shared buffer for efficient rendering.
+ *
+ * @param srv Console server
+ * @param cols Number of columns in buffer
+ * @param rows Number of rows in buffer
+ * @param rbuf Place to store pointer to new sharable buffer
+ *
+ * @return EOK on sucess or an error code
+ */
+static errno_t cons_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
+    charfield_t **rbuf)
+{
+	console_t *cons = srv_to_console(srv);
+	void *buf;
+
+	fibril_mutex_lock(&cons->mtx);
+
+	if (cons->ubuf != NULL) {
+		fibril_mutex_unlock(&cons->mtx);
+		return EBUSY;
+	}
+
+	buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
+	    AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
+	if (buf == AS_MAP_FAILED) {
+		fibril_mutex_unlock(&cons->mtx);
+		return ENOMEM;
+	}
+
+	cons->ucols = cols;
+	cons->urows = rows;
+	cons->ubuf = buf;
+	fibril_mutex_unlock(&cons->mtx);
+
+	*rbuf = buf;
+	return EOK;
+}
+
+/** Delete shared buffer.
+ *
+ * @param srv Console server
+ */
+static void cons_unmap(con_srv_t *srv)
+{
+	console_t *cons = srv_to_console(srv);
+	void *buf;
+
+	fibril_mutex_lock(&cons->mtx);
+
+	buf = cons->ubuf;
+	cons->ubuf = NULL;
+
+	if (buf != NULL)
+		as_area_destroy(buf);
+
+	fibril_mutex_unlock(&cons->mtx);
+}
+
+/** Update area of console from shared buffer.
+ *
+ * @param srv Console server
+ * @param c0 Column coordinate of top-left corner (inclusive)
+ * @param r0 Row coordinate of top-left corner (inclusive)
+ * @param c1 Column coordinate of bottom-right corner (exclusive)
+ * @param r1 Row coordinate of bottom-right corner (exclusive)
+ */
+static void cons_buf_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
+    sysarg_t c1, sysarg_t r1)
+{
+	console_t *cons = srv_to_console(srv);
+	charfield_t *ch;
+	sysarg_t col, row;
+
+	fibril_mutex_lock(&cons->mtx);
+
+	if (cons->ubuf == NULL) {
+		fibril_mutex_unlock(&cons->mtx);
+		return;
+	}
+
+	/* Make sure we have meaningful coordinates, within bounds */
+
+	if (c1 > cons->ucols)
+		c1 = cons->ucols;
+	if (c1 > cons->cols)
+		c1 = cons->cols;
+	if (c0 >= c1) {
+		fibril_mutex_unlock(&cons->mtx);
+		return;
+	}
+	if (r1 > cons->urows)
+		r1 = cons->urows;
+	if (r1 > cons->rows)
+		r1 = cons->rows;
+	if (r0 >= r1) {
+		fibril_mutex_unlock(&cons->mtx);
+		return;
+	}
+
+	/* Update front buffer from user buffer */
+
+	for (row = r0; row < r1; row++) {
+		for (col = c0; col < c1; col++) {
+			ch = chargrid_charfield_at(cons->frontbuf, col, row);
+			*ch = cons->ubuf[row * cons->ucols + col];
+		}
+	}
+
+	fibril_mutex_unlock(&cons->mtx);
+
+	/* Update console */
+	cons_update(cons);
+}
+
 static void client_connection(ipc_call_t *icall, void *arg)
 {
