Index: abi/include/ddi/irq.h
===================================================================
--- abi/include/ddi/irq.h	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ abi/include/ddi/irq.h	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -35,4 +35,9 @@
 #ifndef ABI_DDI_IRQ_H_
 #define ABI_DDI_IRQ_H_
+
+typedef struct {
+	uintptr_t base;
+	size_t size;
+} irq_pio_range_t;
 
 typedef enum {
@@ -97,4 +102,6 @@
 
 typedef struct {
+	size_t rangecount;
+	irq_pio_range_t *ranges;
 	size_t cmdcount;
 	irq_cmd_t *cmds;
Index: kernel/generic/include/ipc/irq.h
===================================================================
--- kernel/generic/include/ipc/irq.h	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ kernel/generic/include/ipc/irq.h	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -36,5 +36,8 @@
 #define KERN_IPC_IRQ_H_
 
-/** Maximum length of IPC IRQ program */
+/** Maximum number of IPC IRQ programmed I/O ranges. */
+#define IRQ_MAX_RANGE_COUNT	8
+
+/** Maximum length of IPC IRQ program. */
 #define IRQ_MAX_PROG_SIZE  20
 
Index: kernel/generic/src/ipc/irq.c
===================================================================
--- kernel/generic/src/ipc/irq.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ kernel/generic/src/ipc/irq.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -74,4 +74,6 @@
 #include <arch.h>
 #include <mm/slab.h>
+#include <mm/page.h>
+#include <mm/km.h>
 #include <errno.h>
 #include <ddi/irq.h>
@@ -81,4 +83,85 @@
 #include <console/console.h>
 #include <print.h>
+#include <macros.h>
+
+static void ranges_unmap(irq_pio_range_t *ranges, size_t rangecount)
+{
+	size_t i;
+
+	for (i = 0; i < rangecount; i++) {
+		if ((void *) ranges[i].base >= IO_SPACE_BOUNDARY)
+			km_unmap(ranges[i].base, ranges[i].size);
+	}
+}
+
+static int ranges_map_and_apply(irq_pio_range_t *ranges, size_t rangecount,
+    irq_cmd_t *cmds, size_t cmdcount)
+{
+	uintptr_t *pbase;
+	size_t i, j;
+
+	/* Copy the physical base addresses aside. */
+	pbase = malloc(rangecount * sizeof(uintptr_t), 0);
+	for (i = 0; i < rangecount; i++)
+		pbase[i] = ranges[i].base;
+
+	/* Map the PIO ranges into the kernel virtual address space. */
+	for (i = 0; i < rangecount; i++) {
+		if ((void *) ranges[i].base < IO_SPACE_BOUNDARY)
+			continue;
+		ranges[i].base = km_map(pbase[i], ranges[i].size,
+		    PAGE_READ | PAGE_WRITE | PAGE_KERNEL | PAGE_NOT_CACHEABLE);
+		if (!ranges[i].base) {
+			ranges_unmap(ranges, i);
+			free(pbase);
+			return ENOMEM;
+		}
+	}
+
+	/* Rewrite the pseudocode addresses from physical to kernel virtual. */
+	for (i = 0; i < cmdcount; i++) {
+		uintptr_t addr;
+
+		/* Process only commands that use an address. */
+		switch (cmds[i].cmd) {
+		case CMD_PIO_READ_8:
+        	case CMD_PIO_READ_16:
+        	case CMD_PIO_READ_32:
+        	case CMD_PIO_WRITE_8:
+        	case CMD_PIO_WRITE_16:
+        	case CMD_PIO_WRITE_32:
+        	case CMD_PIO_WRITE_A_8:
+        	case CMD_PIO_WRITE_A_16:
+        	case CMD_PIO_WRITE_A_32:
+			break;
+		default:
+			/* Move onto the next command. */
+			continue;
+		}
+
+		addr = (uintptr_t) cmds[i].addr;
+		
+		/* Process only memory mapped PIO addresses. */
+		if ((void *) addr < IO_SPACE_BOUNDARY)
+			continue;
+
+		for (j = 0; j < rangecount; j++) {
+
+			/* Find the matching range. */
+			if (!iswithin(pbase[j], ranges[j].size, addr, 1))
+				continue;
+
+			/* Switch the command to a kernel virtual address. */
+			addr -= pbase[j];
+			addr += ranges[j].base;
+
+			cmds[i].addr = (void *) addr;
+			break;
+		} 
+	}
+
+	free(pbase);
+	return EOK;
+}
 
 /** Free the top-half pseudocode.
@@ -90,4 +173,6 @@
 {
 	if (code) {
+		ranges_unmap(code->ranges, code->rangecount);
+		free(code->ranges);
 		free(code->cmds);
 		free(code);
@@ -104,27 +189,45 @@
 static irq_code_t *code_from_uspace(irq_code_t *ucode)
 {
+	irq_pio_range_t *ranges = NULL;
+	irq_cmd_t *cmds = NULL;
+
 	irq_code_t *code = malloc(sizeof(*code), 0);
 	int rc = copy_from_uspace(code, ucode, sizeof(*code));
-	if (rc != 0) {
-		free(code);
-		return NULL;
-	}
-	
-	if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
-		free(code);
-		return NULL;
-	}
-	
-	irq_cmd_t *ucmds = code->cmds;
-	code->cmds = malloc(sizeof(code->cmds[0]) * code->cmdcount, 0);
-	rc = copy_from_uspace(code->cmds, ucmds,
+	if (rc != EOK)
+		goto error;
+	
+	if ((code->rangecount > IRQ_MAX_RANGE_COUNT) ||
+	    (code->cmdcount > IRQ_MAX_PROG_SIZE))
+		goto error;
+	
+	ranges = malloc(sizeof(code->ranges[0]) * code->rangecount, 0);
+	rc = copy_from_uspace(ranges, code->ranges,
+	    sizeof(code->ranges[0]) * code->rangecount);
+	if (rc != EOK)
+		goto error;
+
+	cmds = malloc(sizeof(code->cmds[0]) * code->cmdcount, 0);
+	rc = copy_from_uspace(cmds, code->cmds,
 	    sizeof(code->cmds[0]) * code->cmdcount);
-	if (rc != 0) {
-		free(code->cmds);
-		free(code);
-		return NULL;
-	}
-	
+	if (rc != EOK)
+		goto error;
+
+	rc = ranges_map_and_apply(ranges, code->rangecount, cmds,
+	    code->cmdcount);
+	if (rc != EOK)
+		goto error;
+
+	code->ranges = ranges;
+	code->cmds = cmds;
+
 	return code;
+
+error:
+	if (cmds)
+		free(cmds);
+	if (ranges)
+		free(ranges);
+	free(code);
+	return NULL;
 }
 
@@ -366,8 +469,4 @@
 	for (size_t i = 0; i < code->cmdcount; i++) {
 		uint32_t dstval;
-		void *va;
-		uint8_t val8;
-		uint16_t val16;
-		uint32_t val32;
 		
 		uintptr_t srcarg = code->cmds[i].srcarg;
Index: kernel/generic/src/syscall/copy.c
===================================================================
--- kernel/generic/src/syscall/copy.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ kernel/generic/src/syscall/copy.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -56,5 +56,5 @@
  * @param size Size of the data to be copied.
  *
- * @return 0 on success or error code from @ref errno.h.
+ * @return EOK on success or error code from @ref errno.h.
  */
 int copy_from_uspace(void *dst, const void *uspace_src, size_t size)
@@ -94,5 +94,5 @@
 
 	interrupts_restore(ipl);
-	return !rc ? EPERM : 0;
+	return !rc ? EPERM : EOK;
 }
 
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ uspace/lib/drv/generic/driver.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -80,4 +80,6 @@
 
 static irq_code_t default_pseudocode = {
+	0,
+	NULL,
 	sizeof(default_cmds) / sizeof(irq_cmd_t),
 	default_cmds
Index: uspace/srv/hid/input/port/gxemul.c
===================================================================
--- uspace/srv/hid/input/port/gxemul.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ uspace/srv/hid/input/port/gxemul.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -69,4 +69,6 @@
 
 static irq_code_t gxemul_kbd = {
+	0,	// FIXME
+	NULL,	// FIXME
 	sizeof(gxemul_cmds) / sizeof(irq_cmd_t),
 	gxemul_cmds
Index: uspace/srv/hid/input/port/msim.c
===================================================================
--- uspace/srv/hid/input/port/msim.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ uspace/srv/hid/input/port/msim.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -69,4 +69,6 @@
 
 static irq_code_t msim_kbd = {
+	0,	// FIXME
+	NULL,	// FIXME
 	sizeof(msim_cmds) / sizeof(irq_cmd_t),
 	msim_cmds
Index: uspace/srv/hid/input/port/ns16550.c
===================================================================
--- uspace/srv/hid/input/port/ns16550.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ uspace/srv/hid/input/port/ns16550.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -98,4 +98,6 @@
 
 irq_code_t ns16550_kbd = {
+	0,
+	NULL,
 	sizeof(ns16550_cmds) / sizeof(irq_cmd_t),
 	ns16550_cmds
Index: uspace/srv/hid/input/port/pl050.c
===================================================================
--- uspace/srv/hid/input/port/pl050.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ uspace/srv/hid/input/port/pl050.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -91,4 +91,6 @@
 
 static irq_code_t pl050_kbd = {
+	0,	// FIXME
+	NULL,	// FIXME
 	sizeof(pl050_cmds) / sizeof(irq_cmd_t),
 	pl050_cmds
Index: uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c
===================================================================
--- uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ uspace/srv/hid/s3c24xx_ts/s3c24xx_ts.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -62,4 +62,6 @@
 
 static irq_code_t ts_irq_code = {
+	0,
+	NULL,
 	sizeof(ts_irq_cmds) / sizeof(irq_cmd_t),
 	ts_irq_cmds
Index: uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c
===================================================================
--- uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c	(revision ec12ab893015bb2f77916355e67e90d5be826a58)
+++ uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c	(revision a996ae31af28626eb224d0fc0318932c1f196c19)
@@ -60,4 +60,6 @@
 
 static irq_code_t uart_irq_code = {
+	0,
+	NULL,
 	sizeof(uart_irq_cmds) / sizeof(irq_cmd_t),
 	uart_irq_cmds
