Index: kernel/genarch/src/fb/fb.c
===================================================================
--- kernel/genarch/src/fb/fb.c	(revision c43b89e8cb1c2470611264ffa38272188780d73b)
+++ kernel/genarch/src/fb/fb.c	(revision 53c8d12b7aeb262a2ee3fa75123776f8514862d5)
@@ -56,4 +56,6 @@
 #define INV_COLOR    0xaaaaaa
 
+#define FB_PAGES  8
+
 #define RED(x, bits)    (((x) >> (8 + 8 + 8 - (bits))) & ((1 << (bits)) - 1))
 #define GREEN(x, bits)  (((x) >> (8 + 8 - (bits))) & ((1 << (bits)) - 1))
@@ -70,5 +72,6 @@
 
 #define BB_POS(instance, col, row) \
-	((row) * (instance)->cols + (col))
+	((((instance)->start_row + (row)) % (instance)->rows) * \
+	    (instance)->cols + (col))
 
 #define GLYPH_POS(instance, glyph, y) \
@@ -92,4 +95,5 @@
 	unsigned int yres;
 	
+	/** Number of rows that fit on framebuffer */
 	unsigned int rowtrim;
 	
@@ -101,17 +105,29 @@
 	unsigned int bgscanbytes;
 	
+	/** Number of columns in the backbuffer */
 	unsigned int cols;
+	/** Number of rows in the backbuffer */
 	unsigned int rows;
 	
+	/** Starting row in the cyclic backbuffer */
+	unsigned int start_row;
+	
+	/** Top-most visible row (relative to start_row) */
+	unsigned int offset_row;
+	
+	/** Current backbuffer position */
 	unsigned int position;
 } fb_instance_t;
 
-static void fb_putchar(outdev_t *dev, wchar_t ch);
-static void fb_redraw_internal(fb_instance_t *instance);
-static void fb_redraw(outdev_t *dev);
+static void fb_putchar(outdev_t *, wchar_t);
+static void fb_redraw(outdev_t *);
+static void fb_scroll_up(outdev_t *);
+static void fb_scroll_down(outdev_t *);
 
 static outdev_operations_t fbdev_ops = {
 	.write = fb_putchar,
-	.redraw = fb_redraw
+	.redraw = fb_redraw,
+	.scroll_up = fb_scroll_up,
+	.scroll_down = fb_scroll_down
 };
 
@@ -216,33 +232,39 @@
     unsigned int col, unsigned int row, bool overlay)
 {
-	unsigned int x = COL2X(col);
-	unsigned int y = ROW2Y(row);
-	unsigned int yd;
-	
 	if (!overlay)
 		instance->backbuf[BB_POS(instance, col, row)] = glyph;
 	
+	/* Do not output if the framebuffer is used by user space */
+	if ((instance->parea.mapped) && (!console_override))
+		return;
+	
+	/* Check whether the glyph should be visible */
+	if (row < instance->offset_row)
+		return;
+	
+	unsigned int rel_row = row - instance->offset_row;
+	if (rel_row >= instance->rowtrim)
+		return;
+	
+	unsigned int x = COL2X(col);
+	unsigned int y = ROW2Y(rel_row);
+	
+	for (unsigned int yd = 0; yd < FONT_SCANLINES; yd++)
+		memcpy(&instance->addr[FB_POS(instance, x, y + yd)],
+		    &instance->glyphs[GLYPH_POS(instance, glyph, yd)],
+		    instance->glyphscanline);
+}
+
+/** Scroll screen down by one row
+ *
+ */
+static void screen_scroll(fb_instance_t *instance)
+{
 	if ((!instance->parea.mapped) || (console_override)) {
-		for (yd = 0; yd < FONT_SCANLINES; yd++)
-			memcpy(&instance->addr[FB_POS(instance, x, y + yd)],
-			    &instance->glyphs[GLYPH_POS(instance, glyph, yd)],
-			    instance->glyphscanline);
-	}
-}
-
-/** Scroll screen down by one row
- *
- *
- */
-static void screen_scroll(fb_instance_t *instance)
-{
-	if ((!instance->parea.mapped) || (console_override)) {
-		unsigned int row;
-		
-		for (row = 0; row < instance->rows; row++) {
-			unsigned int y = ROW2Y(row);
-			unsigned int yd;
+		for (unsigned int rel_row = 0; rel_row < instance->rowtrim; rel_row++) {
+			unsigned int y = ROW2Y(rel_row);
+			unsigned int row = rel_row + instance->offset_row;
 			
-			for (yd = 0; yd < FONT_SCANLINES; yd++) {
+			for (unsigned int yd = 0; yd < FONT_SCANLINES; yd++) {
 				unsigned int x;
 				unsigned int col;
@@ -269,6 +291,13 @@
 	}
 	
-	memmove(instance->backbuf, &instance->backbuf[BB_POS(instance, 0, 1)],
-	    instance->cols * (instance->rows - 1) * sizeof(uint16_t));
+	/*
+	 * Implement backbuffer scrolling by wrapping around
+	 * the cyclic buffer.
+	 */
+	
+	instance->start_row++;
+	if (instance->start_row == instance->rows)
+		instance->start_row = 0;
+	
 	memsetw(&instance->backbuf[BB_POS(instance, 0, instance->rows - 1)],
 	    instance->cols, 0);
@@ -334,65 +363,11 @@
 }
 
-/** Print character to screen
- *
- * Emulate basic terminal commands.
- *
- */
-static void fb_putchar(outdev_t *dev, wchar_t ch)
-{
-	fb_instance_t *instance = (fb_instance_t *) dev->data;
-	spinlock_lock(&instance->lock);
-	
-	switch (ch) {
-	case '\n':
-		cursor_remove(instance);
-		instance->position += instance->cols;
-		instance->position -= instance->position % instance->cols;
-		break;
-	case '\r':
-		cursor_remove(instance);
-		instance->position -= instance->position % instance->cols;
-		break;
-	case '\b':
-		cursor_remove(instance);
-		if (instance->position % instance->cols)
-			instance->position--;
-		break;
-	case '\t':
-		cursor_remove(instance);
-		do {
-			glyph_draw(instance, fb_font_glyph(' '),
-			    instance->position % instance->cols,
-			    instance->position / instance->cols, false);
-			instance->position++;
-		} while (((instance->position % instance->cols) % 8 != 0) &&
-		    (instance->position < instance->cols * instance->rows));
-		break;
-	default:
-		glyph_draw(instance, fb_font_glyph(ch),
-		    instance->position % instance->cols,
-		    instance->position / instance->cols, false);
-		instance->position++;
-	}
-	
-	if (instance->position >= instance->cols * instance->rows) {
-		instance->position -= instance->cols;
-		screen_scroll(instance);
-	}
-	
-	cursor_put(instance);
-	
-	spinlock_unlock(&instance->lock);
-}
-
 static void fb_redraw_internal(fb_instance_t *instance)
 {
-	unsigned int row;
-	
-	for (row = 0; row < instance->rowtrim; row++) {
-		unsigned int y = ROW2Y(row);
-		unsigned int yd;
+	for (unsigned int rel_row = 0; rel_row < instance->rowtrim; rel_row++) {
+		unsigned int y = ROW2Y(rel_row);
+		unsigned int row = rel_row + instance->offset_row;
 		
-		for (yd = 0; yd < FONT_SCANLINES; yd++) {
+		for (unsigned int yd = 0; yd < FONT_SCANLINES; yd++) {
 			unsigned int x;
 			unsigned int col;
@@ -428,4 +403,95 @@
 }
 
+/** Print character to screen
+ *
+ * Emulate basic terminal commands.
+ *
+ */
+static void fb_putchar(outdev_t *dev, wchar_t ch)
+{
+	fb_instance_t *instance = (fb_instance_t *) dev->data;
+	spinlock_lock(&instance->lock);
+	
+	switch (ch) {
+	case '\n':
+		cursor_remove(instance);
+		instance->position += instance->cols;
+		instance->position -= instance->position % instance->cols;
+		break;
+	case '\r':
+		cursor_remove(instance);
+		instance->position -= instance->position % instance->cols;
+		break;
+	case '\b':
+		cursor_remove(instance);
+		if (instance->position % instance->cols)
+			instance->position--;
+		break;
+	case '\t':
+		cursor_remove(instance);
+		do {
+			glyph_draw(instance, fb_font_glyph(' '),
+			    instance->position % instance->cols,
+			    instance->position / instance->cols, false);
+			instance->position++;
+		} while (((instance->position % instance->cols) % 8 != 0) &&
+		    (instance->position < instance->cols * instance->rows));
+		break;
+	default:
+		glyph_draw(instance, fb_font_glyph(ch),
+		    instance->position % instance->cols,
+		    instance->position / instance->cols, false);
+		instance->position++;
+	}
+	
+	if (instance->position >= instance->cols * instance->rows) {
+		instance->position -= instance->cols;
+		screen_scroll(instance);
+	}
+	
+	cursor_put(instance);
+	
+	spinlock_unlock(&instance->lock);
+}
+
+/** Scroll the framebuffer up
+ *
+ */
+static void fb_scroll_up(outdev_t *dev)
+{
+	fb_instance_t *instance = (fb_instance_t *) dev->data;
+	spinlock_lock(&instance->lock);
+	
+	if (instance->offset_row >= instance->rowtrim / 2)
+		instance->offset_row -= instance->rowtrim / 2;
+	else
+		instance->offset_row = 0;
+	
+	fb_redraw_internal(instance);
+	cursor_put(instance);
+	
+	spinlock_unlock(&instance->lock);
+}
+
+/** Scroll the framebuffer down
+ *
+ */
+static void fb_scroll_down(outdev_t *dev)
+{
+	fb_instance_t *instance = (fb_instance_t *) dev->data;
+	spinlock_lock(&instance->lock);
+	
+	if (instance->offset_row + instance->rowtrim / 2 <=
+	    instance->rows - instance->rowtrim)
+		instance->offset_row += instance->rowtrim / 2;
+	else
+		instance->offset_row = instance->rows - instance->rowtrim;
+	
+	fb_redraw_internal(instance);
+	cursor_put(instance);
+	
+	spinlock_unlock(&instance->lock);
+}
+
 /** Refresh the screen
  *
@@ -523,10 +589,13 @@
 	instance->yres = props->y;
 	instance->scanline = props->scan;
-	instance->position = 0;
+	
+	instance->rowtrim = Y2ROW(instance->yres);
 	
 	instance->cols = X2COL(instance->xres);
-	instance->rows = Y2ROW(instance->yres);
-	
-	instance->rowtrim = instance->rows;
+	instance->rows = FB_PAGES * instance->rowtrim;
+	
+	instance->start_row = instance->rows - instance->rowtrim;
+	instance->offset_row = instance->start_row;
+	instance->position = instance->start_row * instance->cols;
 	
 	instance->glyphscanline = FONT_WIDTH * instance->pixelbytes;
