Index: boot/arch/amd64/Makefile.inc
===================================================================
--- boot/arch/amd64/Makefile.inc	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ boot/arch/amd64/Makefile.inc	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -52,5 +52,6 @@
 	$(USPACEDIR)/srv/fs/fat/fat \
 	$(USPACEDIR)/srv/bd/ata_bd/ata_bd \
-	$(USPACEDIR)/srv/bd/file_bd/file_bd
+	$(USPACEDIR)/srv/bd/file_bd/file_bd \
+	$(USPACEDIR)/srv/part/mbr_part/mbr_part
 
 RD_APPS = \
Index: boot/arch/arm32/loader/Makefile
===================================================================
--- boot/arch/arm32/loader/Makefile	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ boot/arch/arm32/loader/Makefile	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -93,5 +93,6 @@
 	$(USPACEDIR)/srv/fs/tmpfs/tmpfs \
 	$(USPACEDIR)/srv/fs/fat/fat \
-	$(USPACEDIR)/srv/bd/file_bd/file_bd
+	$(USPACEDIR)/srv/bd/file_bd/file_bd \
+	$(USPACEDIR)/srv/part/mbr_part/mbr_part
 ifeq ($(MACHINE),testarm)
 	RD_SRVS += \
Index: boot/arch/ia32/Makefile.inc
===================================================================
--- boot/arch/ia32/Makefile.inc	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ boot/arch/ia32/Makefile.inc	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -52,5 +52,6 @@
 	$(USPACEDIR)/srv/fs/fat/fat \
 	$(USPACEDIR)/srv/bd/ata_bd/ata_bd \
-	$(USPACEDIR)/srv/bd/file_bd/file_bd
+	$(USPACEDIR)/srv/bd/file_bd/file_bd \
+	$(USPACEDIR)/srv/part/mbr_part/mbr_part
 
 RD_APPS = \
Index: boot/arch/ia64/loader/Makefile
===================================================================
--- boot/arch/ia64/loader/Makefile	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ boot/arch/ia64/loader/Makefile	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -104,5 +104,6 @@
 	$(USPACEDIR)/srv/fs/tmpfs/tmpfs \
 	$(USPACEDIR)/srv/fs/fat/fat \
-	$(USPACEDIR)/srv/bd/file_bd/file_bd
+	$(USPACEDIR)/srv/bd/file_bd/file_bd \
+	$(USPACEDIR)/srv/part/mbr_part/mbr_part
 
 RD_APPS = \
Index: boot/arch/mips32/loader/Makefile
===================================================================
--- boot/arch/mips32/loader/Makefile	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ boot/arch/mips32/loader/Makefile	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -106,5 +106,6 @@
 	$(USPACEDIR)/srv/fs/fat/fat \
 	$(USPACEDIR)/srv/bd/file_bd/file_bd \
-	$(USPACEDIR)/srv/bd/gxe_bd/gxe_bd
+	$(USPACEDIR)/srv/bd/gxe_bd/gxe_bd \
+	$(USPACEDIR)/srv/part/mbr_part/mbr_part
 
 RD_APPS = \
Index: boot/arch/ppc32/loader/Makefile
===================================================================
--- boot/arch/ppc32/loader/Makefile	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ boot/arch/ppc32/loader/Makefile	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -94,5 +94,6 @@
 	$(USPACEDIR)/srv/fs/tmpfs/tmpfs \
 	$(USPACEDIR)/srv/fs/fat/fat \
-	$(USPACEDIR)/srv/bd/file_bd/file_bd
+	$(USPACEDIR)/srv/bd/file_bd/file_bd \
+	$(USPACEDIR)/srv/part/mbr_part/mbr_part
 
 RD_APPS = \
Index: boot/arch/sparc64/loader/Makefile
===================================================================
--- boot/arch/sparc64/loader/Makefile	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ boot/arch/sparc64/loader/Makefile	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -104,5 +104,6 @@
 	$(USPACEDIR)/srv/fs/devfs/devfs \
 	$(USPACEDIR)/srv/fs/tmpfs/tmpfs \
-	$(USPACEDIR)/srv/bd/file_bd/file_bd
+	$(USPACEDIR)/srv/bd/file_bd/file_bd \
+	$(USPACEDIR)/srv/part/mbr_part/mbr_part
 
 ifeq ($(MACHINE),generic)
Index: contrib/bp/srv/fs/fat/fat.bp
===================================================================
--- contrib/bp/srv/fs/fat/fat.bp	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
+++ contrib/bp/srv/fs/fat/fat.bp	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -0,0 +1,45 @@
+!ns.IPC_M_CONNECT_ME_TO /* vfs */ ;
+[../../../lib/libfs/fs_register] ;
+(
+	?IPC_M_CONNECT_ME_TO ;
+	(
+		?VFS_OUT_MOUNTED {
+			?IPC_M_DATA_WRITE /* mount options */
+		} +
+		
+		?VFS_OUT_MOUNT {
+			[../../../lib/libfs/libfs_mount]
+		} +
+		
+		?VFS_OUT_LOOKUP {
+			[../../../lib/libfs/libfs_lookup]
+		} +
+		
+		?VFS_OUT_READ {
+			?IPC_M_DATA_READ /* payload */
+		} +
+		
+		?VFS_OUT_WRITE {
+			?IPC_M_DATA_WRITE /* payload */
+		} +
+		
+		?VFS_OUT_TRUNCATE +
+		
+		?VFS_OUT_CLOSE +
+		
+		?VFS_OUT_DESTROY +
+		
+		?VFS_OUT_OPEN_NODE {
+			[../../../lib/libfs/libfs_open_node]
+		} +
+		
+		?VFS_OUT_STAT {
+			[../../../lib/libfs/libfs_stat]
+		} +
+		
+		?VFS_OUT_SYNC
+		
+	)* ;
+	?IPC_M_PHONE_HUNGUP
+)* ;
+!vfs.IPC_M_PHONE_HUNGUP
Index: contrib/bp/srv/pci/pci.bp
===================================================================
--- contrib/bp/srv/pci/pci.bp	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
+++ contrib/bp/srv/pci/pci.bp	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -0,0 +1,5 @@
+!ns.IPC_M_CONNECT_TO_ME ;
+(
+	?IPC_M_CONNECT_ME_TO ;
+	?IPC_M_PHONE_HUNGUP
+)*
Index: kernel/arch/arm32/include/elf.h
===================================================================
--- kernel/arch/arm32/include/elf.h	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ kernel/arch/arm32/include/elf.h	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -27,5 +27,5 @@
  */
 
-/** @addtogroup arm32	
+/** @addtogroup arm32
  * @{
  */
@@ -37,13 +37,13 @@
 #define KERN_arm32_ELF_H_
 
-#define	ELF_MACHINE		EM_ARM
+#define ELF_MACHINE  EM_ARM
 
-#ifdef BIG_ENDIAN
-#define ELF_DATA_ENCODING	ELFDATA2MSB
+#ifdef __BE__
+	#define ELF_DATA_ENCODING  ELFDATA2MSB
 #else
-#define ELF_DATA_ENCODING	ELFDATA2LSB
+	#define ELF_DATA_ENCODING  ELFDATA2LSB
 #endif
 
-#define ELF_CLASS		ELFCLASS32
+#define ELF_CLASS  ELFCLASS32
 
 #endif
Index: kernel/arch/mips32/include/mm/tlb.h
===================================================================
--- kernel/arch/mips32/include/mm/tlb.h	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ kernel/arch/mips32/include/mm/tlb.h	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -59,5 +59,5 @@
 typedef union {
 	struct {
-#ifdef BIG_ENDIAN
+#ifdef __BE__
 		unsigned : 2;       /* zero */
 		unsigned pfn : 24;  /* frame number */
@@ -80,5 +80,5 @@
 typedef union {
 	struct {
-#ifdef BIG_ENDIAN
+#ifdef __BE__
 		unsigned vpn2 : 19;
 		unsigned : 5;
@@ -95,5 +95,5 @@
 typedef union {
 	struct {
-#ifdef BIG_ENDIAN
+#ifdef __BE__
 		unsigned : 7;
 		unsigned mask : 12;
@@ -110,5 +110,5 @@
 typedef union {
 	struct {
-#ifdef BIG_ENDIAN
+#ifdef __BE__
 		unsigned p : 1;
 		unsigned : 27;
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/Makefile	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -52,4 +52,5 @@
 	srv/vfs \
 	srv/devmap \
+	srv/part/mbr_part \
 	app/tetris \
 	app/tester \
Index: uspace/lib/libblock/libblock.c
===================================================================
--- uspace/lib/libblock/libblock.c	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/lib/libblock/libblock.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -50,4 +50,5 @@
 #include <adt/list.h>
 #include <adt/hash_table.h>
+#include <macros.h>
 #include <mem.h>
 
@@ -62,5 +63,5 @@
 typedef struct {
 	fibril_mutex_t lock;
-	size_t block_size;		/**< Block size. */
+	size_t lblock_size;		/**< Logical block size. */
 	unsigned block_count;		/**< Total number of blocks. */
 	unsigned blocks_cached;		/**< Number of cached blocks. */
@@ -74,15 +75,16 @@
 	dev_handle_t dev_handle;
 	int dev_phone;
-	fibril_mutex_t com_area_lock;
-	void *com_area;
-	size_t com_size;
+	fibril_mutex_t comm_area_lock;
+	void *comm_area;
+	size_t comm_size;
 	void *bb_buf;
-	off_t bb_off;
-	size_t bb_size;
+	bn_t bb_addr;
+	size_t pblock_size;		/**< Physical block size. */
 	cache_t *cache;
 } devcon_t;
 
-static int read_block(devcon_t *devcon, bn_t boff, size_t block_size);
-static int write_block(devcon_t *devcon, bn_t boff, size_t block_size);
+static int read_blocks(devcon_t *devcon, bn_t ba, size_t cnt);
+static int write_blocks(devcon_t *devcon, bn_t ba, size_t cnt);
+static int get_block_size(int dev_phone, size_t *bsize);
 
 static devcon_t *devcon_search(dev_handle_t dev_handle)
@@ -102,9 +104,12 @@
 }
 
-static int devcon_add(dev_handle_t dev_handle, int dev_phone, void *com_area,
-   size_t com_size)
+static int devcon_add(dev_handle_t dev_handle, int dev_phone, size_t bsize,
+    void *comm_area, size_t comm_size)
 {
 	link_t *cur;
 	devcon_t *devcon;
+
+	if (comm_size < bsize)
+		return EINVAL;
 
 	devcon = malloc(sizeof(devcon_t));
@@ -115,10 +120,10 @@
 	devcon->dev_handle = dev_handle;
 	devcon->dev_phone = dev_phone;
-	fibril_mutex_initialize(&devcon->com_area_lock);
-	devcon->com_area = com_area;
-	devcon->com_size = com_size;
+	fibril_mutex_initialize(&devcon->comm_area_lock);
+	devcon->comm_area = comm_area;
+	devcon->comm_size = comm_size;
 	devcon->bb_buf = NULL;
-	devcon->bb_off = 0;
-	devcon->bb_size = 0;
+	devcon->bb_addr = 0;
+	devcon->pblock_size = bsize;
 	devcon->cache = NULL;
 
@@ -144,13 +149,14 @@
 }
 
-int block_init(dev_handle_t dev_handle, size_t com_size)
+int block_init(dev_handle_t dev_handle, size_t comm_size)
 {
 	int rc;
 	int dev_phone;
-	void *com_area;
-	
-	com_area = mmap(NULL, com_size, PROTO_READ | PROTO_WRITE,
+	void *comm_area;
+	size_t bsize;
+
+	comm_area = mmap(NULL, comm_size, PROTO_READ | PROTO_WRITE,
 	    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
-	if (!com_area) {
+	if (!comm_area) {
 		return ENOMEM;
 	}
@@ -158,19 +164,25 @@
 	dev_phone = devmap_device_connect(dev_handle, IPC_FLAG_BLOCKING);
 	if (dev_phone < 0) {
-		munmap(com_area, com_size);
+		munmap(comm_area, comm_size);
 		return dev_phone;
 	}
 
-	rc = ipc_share_out_start(dev_phone, com_area,
+	rc = ipc_share_out_start(dev_phone, comm_area,
 	    AS_AREA_READ | AS_AREA_WRITE);
 	if (rc != EOK) {
-	    	munmap(com_area, com_size);
+	    	munmap(comm_area, comm_size);
 		ipc_hangup(dev_phone);
 		return rc;
 	}
-	
-	rc = devcon_add(dev_handle, dev_phone, com_area, com_size);
+
+	if (get_block_size(dev_phone, &bsize) != EOK) {
+		munmap(comm_area, comm_size);
+		ipc_hangup(dev_phone);
+		return rc;
+	}
+	
+	rc = devcon_add(dev_handle, dev_phone, bsize, comm_area, comm_size);
 	if (rc != EOK) {
-		munmap(com_area, com_size);
+		munmap(comm_area, comm_size);
 		ipc_hangup(dev_phone);
 		return rc;
@@ -195,5 +207,5 @@
 	}
 
-	munmap(devcon->com_area, devcon->com_size);
+	munmap(devcon->comm_area, devcon->comm_size);
 	ipc_hangup(devcon->dev_phone);
 
@@ -201,5 +213,5 @@
 }
 
-int block_bb_read(dev_handle_t dev_handle, off_t off, size_t size)
+int block_bb_read(dev_handle_t dev_handle, bn_t ba)
 {
 	void *bb_buf;
@@ -211,21 +223,20 @@
 	if (devcon->bb_buf)
 		return EEXIST;
-	bb_buf = malloc(size);
+	bb_buf = malloc(devcon->pblock_size);
 	if (!bb_buf)
 		return ENOMEM;
-	
-	fibril_mutex_lock(&devcon->com_area_lock);
-	rc = read_block(devcon, 0, size);
+
+	fibril_mutex_lock(&devcon->comm_area_lock);
+	rc = read_blocks(devcon, 0, 1);
 	if (rc != EOK) {
-		fibril_mutex_unlock(&devcon->com_area_lock);
+		fibril_mutex_unlock(&devcon->comm_area_lock);
 	    	free(bb_buf);
 		return rc;
 	}
-	memcpy(bb_buf, devcon->com_area, size);
-	fibril_mutex_unlock(&devcon->com_area_lock);
+	memcpy(bb_buf, devcon->comm_area, devcon->pblock_size);
+	fibril_mutex_unlock(&devcon->comm_area_lock);
 
 	devcon->bb_buf = bb_buf;
-	devcon->bb_off = off;
-	devcon->bb_size = size;
+	devcon->bb_addr = ba;
 
 	return EOK;
@@ -275,8 +286,11 @@
 	fibril_mutex_initialize(&cache->lock);
 	list_initialize(&cache->free_head);
-	cache->block_size = size;
+	cache->lblock_size = size;
 	cache->block_count = blocks;
 	cache->blocks_cached = 0;
 	cache->mode = mode;
+
+	/* No block size translation a.t.m. */
+	assert(cache->lblock_size == devcon->pblock_size);
 
 	if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
@@ -371,5 +385,5 @@
 			if (!b)
 				goto recycle;
-			b->data = malloc(cache->block_size);
+			b->data = malloc(cache->lblock_size);
 			if (!b->data) {
 				free(b);
@@ -405,9 +419,8 @@
 				list_append(&b->free_link, &cache->free_head);
 				fibril_mutex_unlock(&cache->lock);
-				fibril_mutex_lock(&devcon->com_area_lock);
-				memcpy(devcon->com_area, b->data, b->size);
-				rc = write_block(devcon, b->boff,
-				    cache->block_size);
-				fibril_mutex_unlock(&devcon->com_area_lock);
+				fibril_mutex_lock(&devcon->comm_area_lock);
+				memcpy(devcon->comm_area, b->data, b->size);
+				rc = write_blocks(devcon, b->boff, 1);
+				fibril_mutex_unlock(&devcon->comm_area_lock);
 				if (rc != EOK) {
 					/*
@@ -444,5 +457,5 @@
 		block_initialize(b);
 		b->dev_handle = dev_handle;
-		b->size = cache->block_size;
+		b->size = cache->lblock_size;
 		b->boff = boff;
 		hash_table_insert(&cache->block_hash, &key, &b->hash_link);
@@ -461,8 +474,8 @@
 			 * the new contents from the device.
 			 */
-			fibril_mutex_lock(&devcon->com_area_lock);
-			rc = read_block(devcon, b->boff, cache->block_size);
-			memcpy(b->data, devcon->com_area, cache->block_size);
-			fibril_mutex_unlock(&devcon->com_area_lock);
+			fibril_mutex_lock(&devcon->comm_area_lock);
+			rc = read_blocks(devcon, b->boff, 1);
+			memcpy(b->data, devcon->comm_area, cache->lblock_size);
+			fibril_mutex_unlock(&devcon->comm_area_lock);
 			if (rc != EOK) 
 				b->toxic = true;
@@ -521,8 +534,8 @@
 	if (block->dirty && (block->refcnt == 1) &&
 	    (blocks_cached > CACHE_HI_WATERMARK || mode != CACHE_MODE_WB)) {
-		fibril_mutex_lock(&devcon->com_area_lock);
-		memcpy(devcon->com_area, block->data, block->size);
-		rc = write_block(devcon, block->boff, block->size);
-		fibril_mutex_unlock(&devcon->com_area_lock);
+		fibril_mutex_lock(&devcon->comm_area_lock);
+		memcpy(devcon->comm_area, block->data, block->size);
+		rc = write_blocks(devcon, block->boff, 1);
+		fibril_mutex_unlock(&devcon->comm_area_lock);
 		block->dirty = false;
 	}
@@ -601,12 +614,16 @@
  */
 int block_seqread(dev_handle_t dev_handle, off_t *bufpos, size_t *buflen,
-    off_t *pos, void *dst, size_t size, size_t block_size)
+    off_t *pos, void *dst, size_t size)
 {
 	off_t offset = 0;
 	size_t left = size;
-	devcon_t *devcon = devcon_search(dev_handle);
-	assert(devcon);
-	
-	fibril_mutex_lock(&devcon->com_area_lock);
+	size_t block_size;
+	devcon_t *devcon;
+
+	devcon = devcon_search(dev_handle);
+	assert(devcon);
+	block_size = devcon->pblock_size;
+	
+	fibril_mutex_lock(&devcon->comm_area_lock);
 	while (left > 0) {
 		size_t rd;
@@ -622,5 +639,5 @@
 			 * destination buffer.
 			 */
-			memcpy(dst + offset, devcon->com_area + *bufpos, rd);
+			memcpy(dst + offset, devcon->comm_area + *bufpos, rd);
 			offset += rd;
 			*bufpos += rd;
@@ -633,7 +650,7 @@
 			int rc;
 
-			rc = read_block(devcon, *pos / block_size, block_size);
+			rc = read_blocks(devcon, *pos / block_size, 1);
 			if (rc != EOK) {
-				fibril_mutex_unlock(&devcon->com_area_lock);
+				fibril_mutex_unlock(&devcon->comm_area_lock);
 				return rc;
 			}
@@ -643,30 +660,99 @@
 		}
 	}
-	fibril_mutex_unlock(&devcon->com_area_lock);
+	fibril_mutex_unlock(&devcon->comm_area_lock);
 	
 	return EOK;
 }
 
-/** Read block from block device.
+/** Read blocks directly from device (bypass cache).
+ *
+ * @param dev_handle	Device handle of the block device.
+ * @param ba		Address of first block.
+ * @param cnt		Number of blocks.
+ * @param src		Buffer for storing the data.
+ *
+ * @return		EOK on success or negative error code on failure.
+ */
+int block_read_direct(dev_handle_t dev_handle, bn_t ba, size_t cnt, void *buf)
+{
+	devcon_t *devcon;
+	int rc;
+
+	devcon = devcon_search(dev_handle);
+	assert(devcon);
+	
+	fibril_mutex_lock(&devcon->comm_area_lock);
+
+	rc = read_blocks(devcon, ba, cnt);
+	if (rc == EOK)
+		memcpy(buf, devcon->comm_area, devcon->pblock_size * cnt);
+
+	fibril_mutex_unlock(&devcon->comm_area_lock);
+
+	return rc;
+}
+
+/** Write blocks directly to device (bypass cache).
+ *
+ * @param dev_handle	Device handle of the block device.
+ * @param ba		Address of first block.
+ * @param cnt		Number of blocks.
+ * @param src		The data to be written.
+ *
+ * @return		EOK on success or negative error code on failure.
+ */
+int block_write_direct(dev_handle_t dev_handle, bn_t ba, size_t cnt,
+    const void *data)
+{
+	devcon_t *devcon;
+	int rc;
+
+	devcon = devcon_search(dev_handle);
+	assert(devcon);
+	
+	fibril_mutex_lock(&devcon->comm_area_lock);
+
+	memcpy(devcon->comm_area, data, devcon->pblock_size * cnt);
+	rc = read_blocks(devcon, ba, cnt);
+
+	fibril_mutex_unlock(&devcon->comm_area_lock);
+
+	return rc;
+}
+
+/** Get device block size.
+ *
+ * @param dev_handle	Device handle of the block device.
+ * @param bsize		Output block size.
+ *
+ * @return		EOK on success or negative error code on failure.
+ */
+int block_get_bsize(dev_handle_t dev_handle, size_t *bsize)
+{
+	devcon_t *devcon;
+
+	devcon = devcon_search(dev_handle);
+	assert(devcon);
+	
+	return get_block_size(devcon->dev_phone, bsize);
+}
+
+/** Read blocks from block device.
  *
  * @param devcon	Device connection.
- * @param boff		Block index.
- * @param block_size	Block size.
+ * @param ba		Address of first block.
+ * @param cnt		Number of blocks.
  * @param src		Buffer for storing the data.
  *
  * @return		EOK on success or negative error code on failure.
  */
-static int read_block(devcon_t *devcon, bn_t boff, size_t block_size)
-{
-	ipcarg_t retval;
+static int read_blocks(devcon_t *devcon, bn_t ba, size_t cnt)
+{
 	int rc;
 
 	assert(devcon);
-	rc = async_req_2_1(devcon->dev_phone, BD_READ_BLOCK, boff, block_size,
-	    &retval);
-	if ((rc != EOK) || (retval != EOK))
-		return (rc != EOK ? rc : (int) retval);
-
-	return EOK;
+	rc = async_req_3_0(devcon->dev_phone, BD_READ_BLOCKS, LOWER32(ba),
+	    UPPER32(ba), cnt);
+	return rc;
 }
 
@@ -674,22 +760,31 @@
  *
  * @param devcon	Device connection.
- * @param boff		Block index.
- * @param block_size	Block size.
+ * @param ba		Address of first block.
+ * @param cnt		Number of blocks.
  * @param src		Buffer containing the data to write.
  *
  * @return		EOK on success or negative error code on failure.
  */
-static int write_block(devcon_t *devcon, bn_t boff, size_t block_size)
-{
-	ipcarg_t retval;
+static int write_blocks(devcon_t *devcon, bn_t ba, size_t cnt)
+{
 	int rc;
 
 	assert(devcon);
-	rc = async_req_2_1(devcon->dev_phone, BD_WRITE_BLOCK, boff, block_size,
-	    &retval);
-	if ((rc != EOK) || (retval != EOK))
-		return (rc != EOK ? rc : (int) retval);
-
-	return EOK;
+	rc = async_req_3_0(devcon->dev_phone, BD_WRITE_BLOCKS, LOWER32(ba),
+	    UPPER32(ba), cnt);
+	return rc;
+}
+
+/** Get block size used by the device. */
+static int get_block_size(int dev_phone, size_t *bsize)
+{
+	ipcarg_t bs;
+	int rc;
+
+	rc = async_req_0_1(dev_phone, BD_GET_BLOCK_SIZE, &bs);
+	if (rc == EOK)
+		*bsize = (size_t) bs;
+
+	return rc;
 }
 
Index: uspace/lib/libblock/libblock.h
===================================================================
--- uspace/lib/libblock/libblock.h	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/lib/libblock/libblock.h	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -60,5 +60,5 @@
 #define BLOCK_FLAGS_NOREAD	1
 
-typedef unsigned bn_t;	/**< Block number type. */
+typedef uint64_t bn_t;	/**< Block number type. */
 
 typedef struct block {
@@ -98,5 +98,5 @@
 extern void block_fini(dev_handle_t);
 
-extern int block_bb_read(dev_handle_t, off_t, size_t);
+extern int block_bb_read(dev_handle_t, bn_t);
 extern void *block_bb_get(dev_handle_t);
 
@@ -107,5 +107,9 @@
 
 extern int block_seqread(dev_handle_t, off_t *, size_t *, off_t *, void *,
-    size_t, size_t);
+    size_t);
+
+extern int block_get_bsize(dev_handle_t, size_t *);
+extern int block_read_direct(dev_handle_t, bn_t, size_t, void *);
+extern int block_write_direct(dev_handle_t, bn_t, size_t, const void *);
 
 #endif
Index: uspace/lib/libc/include/ipc/bd.h
===================================================================
--- uspace/lib/libc/include/ipc/bd.h	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/lib/libc/include/ipc/bd.h	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -39,6 +39,7 @@
 
 typedef enum {
-	BD_READ_BLOCK = IPC_FIRST_USER_METHOD,
-	BD_WRITE_BLOCK
+	BD_GET_BLOCK_SIZE = IPC_FIRST_USER_METHOD,
+	BD_READ_BLOCKS,
+	BD_WRITE_BLOCKS
 } bd_request_t;
 
Index: uspace/srv/bd/ata_bd/ata_bd.c
===================================================================
--- uspace/srv/bd/ata_bd/ata_bd.c	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/srv/bd/ata_bd/ata_bd.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -62,4 +62,5 @@
 #include <bool.h>
 #include <task.h>
+#include <macros.h>
 
 #include "ata_bd.h"
@@ -86,14 +87,16 @@
 static int ata_bd_init(void);
 static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
-static int ata_bd_rdwr(int disk_id, ipcarg_t method, off_t offset, size_t size,
+static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
     void *buf);
-static int ata_bd_read_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
+static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
+    const void *buf);
+static int ata_bd_read_block(int disk_id, uint64_t ba, size_t cnt,
     void *buf);
-static int ata_bd_write_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
+static int ata_bd_write_block(int disk_id, uint64_t ba, size_t cnt,
     const void *buf);
 static int disk_init(disk_t *d, int disk_id);
 static int drive_identify(int drive_id, void *buf);
 static void disk_print_summary(disk_t *d);
-static int coord_calc(disk_t *d, uint64_t blk_idx, block_coord_t *bc);
+static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc);
 static void coord_sc_program(const block_coord_t *bc, uint16_t scnt);
 static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
@@ -228,6 +231,6 @@
 	int flags;
 	int retval;
-	off_t idx;
-	size_t size;
+	uint64_t ba;
+	size_t cnt;
 	int disk_id, i;
 
@@ -270,15 +273,27 @@
 			ipc_answer_0(callid, EOK);
 			return;
-		case BD_READ_BLOCK:
-		case BD_WRITE_BLOCK:
-			idx = IPC_GET_ARG1(call);
-			size = IPC_GET_ARG2(call);
-			if (size > comm_size) {
-				retval = EINVAL;
+		case BD_READ_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
+				retval = ELIMIT;
 				break;
 			}
-			retval = ata_bd_rdwr(disk_id, method, idx,
-			    size, fs_va);
+			retval = ata_bd_read_blocks(disk_id, ba, cnt, fs_va);
 			break;
+		case BD_WRITE_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
+				retval = ELIMIT;
+				break;
+			}
+			retval = ata_bd_write_blocks(disk_id, ba, cnt, fs_va);
+			break;
+		case BD_GET_BLOCK_SIZE:
+			ipc_answer_1(callid, EOK, block_size);
+			continue;
 		default:
 			retval = EINVAL;
@@ -373,40 +388,37 @@
 }
 
-/** Transfer a logical block from/to the device.
- *
- * @param disk_id	Device index (0 or 1)
- * @param method	@c BD_READ_BLOCK or @c BD_WRITE_BLOCK
- * @param blk_idx	Index of the first block.
- * @param size		Size of the logical block.
- * @param buf		Data buffer.
- *
- * @return EOK on success, EIO on error.
- */
-static int ata_bd_rdwr(int disk_id, ipcarg_t method, off_t blk_idx, size_t size,
-    void *buf)
-{
+/** Read multiple blocks from the device. */
+static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
+    void *buf) {
+
 	int rc;
-	size_t now;
-
-	while (size > 0) {
-		now = size < block_size ? size : block_size;
-		if (now != block_size)
-			return EINVAL;
-
-		if (method == BD_READ_BLOCK)
-			rc = ata_bd_read_block(disk_id, blk_idx, 1, buf);
-		else
-			rc = ata_bd_write_block(disk_id, blk_idx, 1, buf);
-
+
+	while (cnt > 0) {
+		rc = ata_bd_read_block(disk_id, ba, 1, buf);
 		if (rc != EOK)
 			return rc;
 
+		++ba;
+		--cnt;
 		buf += block_size;
-		blk_idx++;
-
-		if (size > block_size)
-			size -= block_size;
-		else
-			size = 0;
+	}
+
+	return EOK;
+}
+
+/** Write multiple blocks to the device. */
+static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
+    const void *buf) {
+
+	int rc;
+
+	while (cnt > 0) {
+		rc = ata_bd_write_block(disk_id, ba, 1, buf);
+		if (rc != EOK)
+			return rc;
+
+		++ba;
+		--cnt;
+		buf += block_size;
 	}
 
@@ -466,11 +478,11 @@
  *
  * @param disk_id	Device index (0 or 1)
- * @param blk_idx	Index of the first block.
- * @param blk_cnt	Number of blocks to transfer.
+ * @param ba		Address the first block.
+ * @param cnt		Number of blocks to transfer.
  * @param buf		Buffer for holding the data.
  *
  * @return EOK on success, EIO on error.
  */
-static int ata_bd_read_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
+static int ata_bd_read_block(int disk_id, uint64_t ba, size_t blk_cnt,
     void *buf)
 {
@@ -486,5 +498,5 @@
 
 	/* Compute block coordinates. */
-	if (coord_calc(d, blk_idx, &bc) != EOK)
+	if (coord_calc(d, ba, &bc) != EOK)
 		return EINVAL;
 
@@ -541,11 +553,11 @@
  *
  * @param disk_id	Device index (0 or 1)
- * @param blk_idx	Index of the first block.
- * @param blk_cnt	Number of blocks to transfer.
+ * @param ba		Address of the first block.
+ * @param cnt		Number of blocks to transfer.
  * @param buf		Buffer holding the data to write.
  *
  * @return EOK on success, EIO on error.
  */
-static int ata_bd_write_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
+static int ata_bd_write_block(int disk_id, uint64_t ba, size_t cnt,
     const void *buf)
 {
@@ -560,5 +572,5 @@
 
 	/* Compute block coordinates. */
-	if (coord_calc(d, blk_idx, &bc) != EOK)
+	if (coord_calc(d, ba, &bc) != EOK)
 		return EINVAL;
 
@@ -620,5 +632,5 @@
  * @return EOK on success or EINVAL if block index is past end of device.
  */
-static int coord_calc(disk_t *d, uint64_t blk_idx, block_coord_t *bc)
+static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc)
 {
 	uint64_t c;
@@ -626,5 +638,5 @@
 
 	/* Check device bounds. */
-	if (blk_idx >= d->blocks)
+	if (ba >= d->blocks)
 		return EINVAL;
 
@@ -634,6 +646,6 @@
 	case am_chs:
 		/* Compute CHS coordinates. */
-		c = blk_idx / (d->geom.heads * d->geom.sectors);
-		idx = blk_idx % (d->geom.heads * d->geom.sectors);
+		c = ba / (d->geom.heads * d->geom.sectors);
+		idx = ba % (d->geom.heads * d->geom.sectors);
 
 		bc->cyl_lo = c & 0xff;
@@ -645,18 +657,18 @@
 	case am_lba28:
 		/* Compute LBA-28 coordinates. */
-		bc->c0 = blk_idx & 0xff;		/* bits 0-7 */
-		bc->c1 = (blk_idx >> 8) & 0xff;		/* bits 8-15 */
-		bc->c2 = (blk_idx >> 16) & 0xff;	/* bits 16-23 */
-		bc->h  = (blk_idx >> 24) & 0x0f;	/* bits 24-27 */
+		bc->c0 = ba & 0xff;		/* bits 0-7 */
+		bc->c1 = (ba >> 8) & 0xff;	/* bits 8-15 */
+		bc->c2 = (ba >> 16) & 0xff;	/* bits 16-23 */
+		bc->h  = (ba >> 24) & 0x0f;	/* bits 24-27 */
 		break;
 
 	case am_lba48:
 		/* Compute LBA-48 coordinates. */
-		bc->c0 = blk_idx & 0xff;		/* bits 0-7 */
-		bc->c1 = (blk_idx >> 8) & 0xff;		/* bits 8-15 */
-		bc->c2 = (blk_idx >> 16) & 0xff;	/* bits 16-23 */
-		bc->c3 = (blk_idx >> 24) & 0xff;	/* bits 24-31 */
-		bc->c4 = (blk_idx >> 32) & 0xff;	/* bits 32-39 */
-		bc->c5 = (blk_idx >> 40) & 0xff;	/* bits 40-47 */
+		bc->c0 = ba & 0xff;		/* bits 0-7 */
+		bc->c1 = (ba >> 8) & 0xff;	/* bits 8-15 */
+		bc->c2 = (ba >> 16) & 0xff;	/* bits 16-23 */
+		bc->c3 = (ba >> 24) & 0xff;	/* bits 24-31 */
+		bc->c4 = (ba >> 32) & 0xff;	/* bits 32-39 */
+		bc->c5 = (ba >> 40) & 0xff;	/* bits 40-47 */
 		bc->h  = 0;
 		break;
Index: uspace/srv/bd/file_bd/file_bd.c
===================================================================
--- uspace/srv/bd/file_bd/file_bd.c	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/srv/bd/file_bd/file_bd.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -51,8 +51,9 @@
 #include <bool.h>
 #include <task.h>
+#include <macros.h>
 
 #define NAME "file_bd"
 
-static size_t comm_size;
+static const size_t block_size = 512;
 static FILE *img;
 
@@ -62,6 +63,6 @@
 static int file_bd_init(const char *fname);
 static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
-static int file_bd_read(off_t blk_idx, size_t size, void *buf);
-static int file_bd_write(off_t blk_idx, size_t size, void *buf);
+static int file_bd_read_blocks(uint64_t ba, size_t cnt, void *buf);
+static int file_bd_write_blocks(uint64_t ba, size_t cnt, const void *buf);
 
 int main(int argc, char **argv)
@@ -120,8 +121,9 @@
 	ipc_call_t call;
 	ipcarg_t method;
+	size_t comm_size;
 	int flags;
 	int retval;
-	off_t idx;
-	size_t size;
+	uint64_t ba;
+	size_t cnt;
 
 	/* Answer the IPC_M_CONNECT_ME_TO call. */
@@ -149,17 +151,27 @@
 			ipc_answer_0(callid, EOK);
 			return;
-		case BD_READ_BLOCK:
-		case BD_WRITE_BLOCK:
-			idx = IPC_GET_ARG1(call);
-			size = IPC_GET_ARG2(call);
-			if (size > comm_size) {
-				retval = EINVAL;
+		case BD_READ_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
+				retval = ELIMIT;
 				break;
 			}
-			if (method == BD_READ_BLOCK)
-				retval = file_bd_read(idx, size, fs_va);
-			else
-				retval = file_bd_write(idx, size, fs_va);
+			retval = file_bd_read_blocks(ba, cnt, fs_va);
 			break;
+		case BD_WRITE_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
+				retval = ELIMIT;
+				break;
+			}
+			retval = file_bd_write_blocks(ba, cnt, fs_va);
+			break;
+		case BD_GET_BLOCK_SIZE:
+			ipc_answer_1(callid, EOK, block_size);
+			continue;
 		default:
 			retval = EINVAL;
@@ -170,5 +182,6 @@
 }
 
-static int file_bd_read(off_t blk_idx, size_t size, void *buf)
+/** Read blocks from the device. */
+static int file_bd_read_blocks(uint64_t ba, size_t cnt, void *buf)
 {
 	size_t n_rd;
@@ -176,6 +189,6 @@
 	fibril_mutex_lock(&dev_lock);
 
-	fseek(img, blk_idx * size, SEEK_SET);
-	n_rd = fread(buf, 1, size, img);
+	fseek(img, ba * block_size, SEEK_SET);
+	n_rd = fread(buf, block_size, cnt, img);
 
 	if (ferror(img)) {
@@ -186,11 +199,12 @@
 	fibril_mutex_unlock(&dev_lock);
 
-	if (n_rd < size) 
-		return EINVAL;	/* Read beyond end of disk */
+	if (n_rd < cnt)
+		return EINVAL;	/* Read beyond end of device */
 
 	return EOK;
 }
 
-static int file_bd_write(off_t blk_idx, size_t size, void *buf)
+/** Write blocks to the device. */
+static int file_bd_write_blocks(uint64_t ba, size_t cnt, const void *buf)
 {
 	size_t n_wr;
@@ -198,8 +212,8 @@
 	fibril_mutex_lock(&dev_lock);
 
-	fseek(img, blk_idx * size, SEEK_SET);
-	n_wr = fread(buf, 1, size, img);
-
-	if (ferror(img) || n_wr < size) {
+	fseek(img, ba * block_size, SEEK_SET);
+	n_wr = fread(buf, block_size, cnt, img);
+
+	if (ferror(img) || n_wr < cnt) {
 		fibril_mutex_unlock(&dev_lock);
 		return EIO;	/* Write error */
Index: uspace/srv/bd/gxe_bd/gxe_bd.c
===================================================================
--- uspace/srv/bd/gxe_bd/gxe_bd.c	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/srv/bd/gxe_bd/gxe_bd.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -47,4 +47,5 @@
 #include <sys/types.h>
 #include <errno.h>
+#include <macros.h>
 #include <task.h>
 
@@ -97,10 +98,10 @@
 static int gxe_bd_init(void);
 static void gxe_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
-static int gx_bd_rdwr(int disk_id, ipcarg_t method, off_t offset, size_t size,
+static int gxe_bd_read_blocks(int disk_id, uint64_t ba, unsigned cnt,
     void *buf);
-static int gxe_bd_read_block(int disk_id, uint64_t offset, size_t size,
-    void *buf);
-static int gxe_bd_write_block(int disk_id, uint64_t offset, size_t size,
+static int gxe_bd_write_blocks(int disk_id, uint64_t ba, unsigned cnt,
     const void *buf);
+static int gxe_bd_read_block(int disk_id, uint64_t ba, void *buf);
+static int gxe_bd_write_block(int disk_id, uint64_t ba, const void *buf);
 
 int main(int argc, char **argv)
@@ -163,6 +164,6 @@
 	int flags;
 	int retval;
-	off_t idx;
-	size_t size;
+	uint64_t ba;
+	unsigned cnt;
 	int disk_id, i;
 
@@ -185,4 +186,9 @@
 
 	if (!ipc_share_out_receive(&callid, &comm_size, &flags)) {
+		ipc_answer_0(callid, EHANGUP);
+		return;
+	}
+
+	if (comm_size < block_size) {
 		ipc_answer_0(callid, EHANGUP);
 		return;
@@ -205,15 +211,27 @@
 			ipc_answer_0(callid, EOK);
 			return;
-		case BD_READ_BLOCK:
-		case BD_WRITE_BLOCK:
-			idx = IPC_GET_ARG1(call);
-			size = IPC_GET_ARG2(call);
-			if (size > comm_size) {
-				retval = EINVAL;
+		case BD_READ_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
+				retval = ELIMIT;
 				break;
 			}
-			retval = gx_bd_rdwr(disk_id, method, idx * size,
-			    size, fs_va);
+			retval = gxe_bd_read_blocks(disk_id, ba, cnt, fs_va);
 			break;
+		case BD_WRITE_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
+				retval = ELIMIT;
+				break;
+			}
+			retval = gxe_bd_write_blocks(disk_id, ba, cnt, fs_va);
+			break;
+		case BD_GET_BLOCK_SIZE:
+			ipc_answer_1(callid, EOK, block_size);
+			continue;
 		default:
 			retval = EINVAL;
@@ -224,43 +242,55 @@
 }
 
-static int gx_bd_rdwr(int disk_id, ipcarg_t method, off_t offset, size_t size,
-    void *buf)
-{
+/** Read multiple blocks from the device. */
+static int gxe_bd_read_blocks(int disk_id, uint64_t ba, unsigned cnt,
+    void *buf) {
+
 	int rc;
-	size_t now;
-
-	while (size > 0) {
-		now = size < block_size ? size : block_size;
-
-		if (method == BD_READ_BLOCK)
-			rc = gxe_bd_read_block(disk_id, offset, now, buf);
-		else
-			rc = gxe_bd_write_block(disk_id, offset, now, buf);
-
+
+	while (cnt > 0) {
+		rc = gxe_bd_read_block(disk_id, ba, buf);
 		if (rc != EOK)
 			return rc;
 
+		++ba;
+		--cnt;
 		buf += block_size;
-		offset += block_size;
-
-		if (size > block_size)
-			size -= block_size;
-		else
-			size = 0;
-	}
-
-	return EOK;
-}
-
-static int gxe_bd_read_block(int disk_id, uint64_t offset, size_t size,
-    void *buf)
+	}
+
+	return EOK;
+}
+
+/** Write multiple blocks to the device. */
+static int gxe_bd_write_blocks(int disk_id, uint64_t ba, unsigned cnt,
+    const void *buf) {
+
+	int rc;
+
+	while (cnt > 0) {
+		rc = gxe_bd_write_block(disk_id, ba, buf);
+		if (rc != EOK)
+			return rc;
+
+		++ba;
+		--cnt;
+		buf += block_size;
+	}
+
+	return EOK;
+}
+
+/** Read a block from the device. */
+static int gxe_bd_read_block(int disk_id, uint64_t ba, void *buf)
 {
 	uint32_t status;
+	uint64_t byte_addr;
 	size_t i;
 	uint32_t w;
 
+	byte_addr = ba * block_size;
+
 	fibril_mutex_lock(&dev_lock[disk_id]);
-	pio_write_32(&dev->offset_lo, (uint32_t) offset);
-	pio_write_32(&dev->offset_hi, offset >> 32);
+	pio_write_32(&dev->offset_lo, (uint32_t) byte_addr);
+	pio_write_32(&dev->offset_hi, byte_addr >> 32);
 	pio_write_32(&dev->disk_id, disk_id);
 	pio_write_32(&dev->control, CTL_READ_START);
@@ -272,5 +302,5 @@
 	}
 
-	for (i = 0; i < size; i++) {
+	for (i = 0; i < block_size; i++) {
 		((uint8_t *) buf)[i] = w = pio_read_8(&dev->buffer[i]);
 	}
@@ -280,17 +310,21 @@
 }
 
-static int gxe_bd_write_block(int disk_id, uint64_t offset, size_t size,
-    const void *buf)
+/** Write a block to the device. */
+static int gxe_bd_write_block(int disk_id, uint64_t ba, const void *buf)
 {
 	uint32_t status;
+	uint64_t byte_addr;
 	size_t i;
 
-	for (i = 0; i < size; i++) {
+	byte_addr = ba * block_size;
+
+	fibril_mutex_lock(&dev_lock[disk_id]);
+
+	for (i = 0; i < block_size; i++) {
 		pio_write_8(&dev->buffer[i], ((const uint8_t *) buf)[i]);
 	}
 
-	fibril_mutex_lock(&dev_lock[disk_id]);
-	pio_write_32(&dev->offset_lo, (uint32_t) offset);
-	pio_write_32(&dev->offset_hi, offset >> 32);
+	pio_write_32(&dev->offset_lo, (uint32_t) byte_addr);
+	pio_write_32(&dev->offset_hi, byte_addr >> 32);
 	pio_write_32(&dev->disk_id, disk_id);
 	pio_write_32(&dev->control, CTL_WRITE_START);
Index: uspace/srv/bd/rd/rd.c
===================================================================
--- uspace/srv/bd/rd/rd.c	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/srv/bd/rd/rd.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -55,11 +55,18 @@
 #include <devmap.h>
 #include <ipc/bd.h>
+#include <macros.h>
 
 #define NAME "rd"
 
-/** Pointer to the ramdisk's image. */
+/** Pointer to the ramdisk's image */
 static void *rd_addr;
-/** Size of the ramdisk. */
+/** Size of the ramdisk */
 static size_t rd_size;
+
+/** Block size */
+static const size_t block_size = 512;
+
+static int rd_read_blocks(uint64_t ba, size_t cnt, void *buf);
+static int rd_write_blocks(uint64_t ba, size_t cnt, const void *buf);
 
 /**
@@ -82,7 +89,7 @@
 	int retval;
 	void *fs_va = NULL;
-	off_t offset;
-	size_t block_size;
-	size_t maxblock_size;
+	uint64_t ba;
+	size_t cnt;
+	size_t comm_size;
 
 	/*
@@ -95,6 +102,6 @@
 	 */
 	int flags;
-	if (ipc_share_out_receive(&callid, &maxblock_size, &flags)) {
-		fs_va = as_get_mappable_page(maxblock_size);
+	if (ipc_share_out_receive(&callid, &comm_size, &flags)) {
+		fs_va = as_get_mappable_page(comm_size);
 		if (fs_va) {
 			(void) ipc_share_out_finalize(callid, fs_va);
@@ -123,48 +130,27 @@
 			ipc_answer_0(callid, EOK);
 			return;
-		case BD_READ_BLOCK:
-			offset = IPC_GET_ARG1(call);
-			block_size = IPC_GET_ARG2(call);
-			if (block_size > maxblock_size) {
-				/*
-				 * Maximum block size exceeded.
-				 */
+		case BD_READ_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
 				retval = ELIMIT;
 				break;
 			}
-			if (offset * block_size > rd_size - block_size) {
-				/*
-				 * Reading past the end of the device.
-				 */
+			retval = rd_read_blocks(ba, cnt, fs_va);
+			break;
+		case BD_WRITE_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
 				retval = ELIMIT;
 				break;
 			}
-			fibril_rwlock_read_lock(&rd_lock);
-			memcpy(fs_va, rd_addr + offset * block_size, block_size);
-			fibril_rwlock_read_unlock(&rd_lock);
-			retval = EOK;
+			retval = rd_write_blocks(ba, cnt, fs_va);
 			break;
-		case BD_WRITE_BLOCK:
-			offset = IPC_GET_ARG1(call);
-			block_size = IPC_GET_ARG2(call);
-			if (block_size > maxblock_size) {
-				/*
-				 * Maximum block size exceeded.
-				 */
-				retval = ELIMIT;
-				break;
-			}
-			if (offset * block_size > rd_size - block_size) {
-				/*
-				 * Writing past the end of the device.
-				 */
-				retval = ELIMIT;
-				break;
-			}
-			fibril_rwlock_write_lock(&rd_lock);
-			memcpy(rd_addr + offset * block_size, fs_va, block_size);
-			fibril_rwlock_write_unlock(&rd_lock);
-			retval = EOK;
-			break;
+		case BD_GET_BLOCK_SIZE:
+			ipc_answer_1(callid, EOK, block_size);
+			continue;
 		default:
 			/*
@@ -181,4 +167,34 @@
 }
 
+/** Read blocks from the device. */
+static int rd_read_blocks(uint64_t ba, size_t cnt, void *buf)
+{
+	if ((ba + cnt) * block_size > rd_size) {
+		/* Reading past the end of the device. */
+		return ELIMIT;
+	}
+
+	fibril_rwlock_read_lock(&rd_lock);
+	memcpy(buf, rd_addr + ba * block_size, block_size * cnt);
+	fibril_rwlock_read_unlock(&rd_lock);
+
+	return EOK;
+}
+
+/** Write blocks to the device. */
+static int rd_write_blocks(uint64_t ba, size_t cnt, const void *buf)
+{
+	if ((ba + cnt) * block_size > rd_size) {
+		/* Writing past the end of the device. */
+		return ELIMIT;
+	}
+
+	fibril_rwlock_write_lock(&rd_lock);
+	memcpy(rd_addr + ba * block_size, buf, block_size * cnt);
+	fibril_rwlock_write_unlock(&rd_lock);
+
+	return EOK;
+}
+
 /** Prepare the ramdisk image for operation. */
 static bool rd_init(void)
Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/srv/fs/fat/fat_ops.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -818,5 +818,5 @@
 
 	/* prepare the boot block */
-	rc = block_bb_read(dev_handle, BS_BLOCK * BS_SIZE, BS_SIZE);
+	rc = block_bb_read(dev_handle, BS_BLOCK);
 	if (rc != EOK) {
 		block_fini(dev_handle);
Index: uspace/srv/fs/tmpfs/tmpfs_dump.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_dump.c	(revision 7a56b1ed5365c3945bf6edad3933a0b1ae29db25)
+++ uspace/srv/fs/tmpfs/tmpfs_dump.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -47,5 +47,5 @@
 #include <byteorder.h>
 
-#define TMPFS_BLOCK_SIZE	1024
+#define TMPFS_COMM_SIZE		1024
 
 struct rdentry {
@@ -69,5 +69,5 @@
 		
 		if (block_seqread(dev, bufpos, buflen, pos, &entry,
-		    sizeof(entry), TMPFS_BLOCK_SIZE) != EOK)
+		    sizeof(entry)) != EOK)
 			return false;
 		
@@ -89,5 +89,5 @@
 			
 			if (block_seqread(dev, bufpos, buflen, pos, fname,
-			    entry.len, TMPFS_BLOCK_SIZE) != EOK) {
+			    entry.len) != EOK) {
 				ops->destroy(fn);
 				free(fname);
@@ -105,5 +105,5 @@
 			
 			if (block_seqread(dev, bufpos, buflen, pos, &size,
-			    sizeof(size), TMPFS_BLOCK_SIZE) != EOK)
+			    sizeof(size)) != EOK)
 				return false;
 			
@@ -117,5 +117,5 @@
 			nodep->size = size;
 			if (block_seqread(dev, bufpos, buflen, pos, nodep->data,
-			    size, TMPFS_BLOCK_SIZE) != EOK)
+			    size) != EOK)
 				return false;
 			
@@ -133,5 +133,5 @@
 			
 			if (block_seqread(dev, bufpos, buflen, pos, fname,
-			    entry.len, TMPFS_BLOCK_SIZE) != EOK) {
+			    entry.len) != EOK) {
 				ops->destroy(fn);
 				free(fname);
@@ -166,5 +166,5 @@
 	int rc;
 
-	rc = block_init(dev, TMPFS_BLOCK_SIZE);
+	rc = block_init(dev, TMPFS_COMM_SIZE);
 	if (rc != EOK)
 		return false; 
@@ -175,6 +175,5 @@
 	
 	char tag[6];
-	if (block_seqread(dev, &bufpos, &buflen, &pos, tag, 5,
-	    TMPFS_BLOCK_SIZE) != EOK)
+	if (block_seqread(dev, &bufpos, &buflen, &pos, tag, 5) != EOK)
 		goto error;
 	
Index: uspace/srv/part/mbr_part/Makefile
===================================================================
--- uspace/srv/part/mbr_part/Makefile	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
+++ uspace/srv/part/mbr_part/Makefile	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -0,0 +1,78 @@
+#
+# Copyright (c) 2006 Martin Decky
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+## Setup toolchain
+#
+
+
+LIBC_PREFIX = ../../../lib/libc
+SOFTINT_PREFIX = ../../../lib/softint
+LIBBLOCK_PREFIX = ../../../lib/libblock
+
+include $(LIBC_PREFIX)/Makefile.toolchain
+
+CFLAGS += -I$(LIBBLOCK_PREFIX)
+LIBS = $(LIBBLOCK_PREFIX)/libblock.a $(LIBC_PREFIX)/libc.a
+
+## Sources
+#
+
+OUTPUT = mbr_part
+SOURCES = \
+	mbr_part.c
+
+OBJECTS := $(addsuffix .o,$(basename $(SOURCES)))
+
+.PHONY: all clean depend disasm
+
+all: $(OUTPUT) $(OUTPUT).disasm
+
+-include Makefile.depend
+
+clean:
+	-rm -f $(OUTPUT) $(OUTPUT).map $(OUTPUT).disasm Makefile.depend $(OBJECTS)
+
+depend:
+	$(CC) $(DEFS) $(CFLAGS) -M $(SOURCES) > Makefile.depend
+
+$(OUTPUT): $(OBJECTS) $(LIBS)
+	$(LD) -T $(LIBC_PREFIX)/arch/$(UARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
+
+disasm: $(OUTPUT).disasm
+
+$(OUTPUT).disasm: $(OUTPUT)
+	$(OBJDUMP) -d $< > $@
+
+%.o: %.S
+	$(CC) $(DEFS) $(AFLAGS) $(CFLAGS) -D__ASM__ -c $< -o $@
+
+%.o: %.s
+	$(AS) $(AFLAGS) $< -o $@
+
+%.o: %.c
+	$(CC) $(DEFS) $(CFLAGS) -c $< -o $@
Index: uspace/srv/part/mbr_part/mbr_part.c
===================================================================
--- uspace/srv/part/mbr_part/mbr_part.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
+++ uspace/srv/part/mbr_part/mbr_part.c	(revision cd3e6a275682a4a2fa5f31da2e726a600264c173)
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2009 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup bd
+ * @{
+ */
+
+/**
+ * @file
+ * @brief PC MBR partition driver
+ *
+ * Handles the PC MBR partitioning scheme. Uses a block device
+ * and provides one for each partition.
+ *
+ * Only works with boot records using LBA. CHS-only records are not
+ * supported. Only primary partition records are supported.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <ipc/ipc.h>
+#include <ipc/bd.h>
+#include <async.h>
+#include <as.h>
+#include <fibril_sync.h>
+#include <devmap.h>
+#include <sys/types.h>
+#include <libblock.h>
+#include <devmap.h>
+#include <errno.h>
+#include <bool.h>
+#include <byteorder.h>
+#include <macros.h>
+#include <task.h>
+
+#define NAME "mbr_part"
+
+enum {
+	/** Number of primary partition records */
+	N_PRIMARY	= 4,
+
+	/** Boot record signature */
+	BR_SIGNATURE	= 0xAA55
+};
+
+/** Partition */
+typedef struct {
+	/** Primary partition entry is in use */
+	bool present;
+	/** Address of first block */
+	bn_t start_addr;
+	/** Number of blocks */
+	bn_t length;
+	/** Device representing the partition (outbound device) */
+	dev_handle_t dev;
+} part_t;
+
+/** Structure of a partition table entry */
+typedef struct {
+	uint8_t status;
+	/** CHS of fist block in partition */
+	uint8_t first_chs[3];
+	/** CHS of last block in partition */
+	uint8_t last_chs[3];
+	/** Partition type */
+	uint8_t ptype;
+	/** LBA of first block in partition */
+	uint32_t first_lba;
+	/** Number of blocks in partition */
+	uint32_t length;
+} __attribute__((packed)) pt_entry_t;
+
+/** Structure of a boot-record block */
+typedef struct {
+	/* Area for boot code */
+	uint8_t code_area[440];
+
+	/* Optional media ID */
+	uint32_t media_id;
+
+	uint16_t pad0;
+
+	/** Partition table entries */
+	pt_entry_t pte[N_PRIMARY];
+
+	/** Boot record block signature (@c BR_SIGNATURE) */
+	uint16_t signature;
+} __attribute__((packed)) br_block_t;
+
+
+static size_t block_size;
+
+/** Partitioned device (inbound device) */
+static dev_handle_t indev_handle;
+
+static part_t primary[N_PRIMARY];
+
+static int mbr_init(const char *dev_name);
+static int mbr_part_read(void);
+static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall);
+static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf);
+static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf);
+static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba);
+
+
+int main(int argc, char **argv)
+{
+	printf(NAME ": PC MBR partition driver\n");
+	printf(NAME ": Size of br_block_t = %d\n", sizeof(br_block_t));
+
+	if (argc != 2) {
+		printf("Expected one argument (device name).\n");
+		return -1;
+	}
+
+	if (mbr_init(argv[1]) != EOK)
+		return -1;
+
+	printf(NAME ": Accepting connections\n");
+	task_retval(0);
+	async_manager();
+
+	/* Not reached */
+	return 0;
+}
+
+static int mbr_init(const char *dev_name)
+{
+	int rc;
+	int i;
+	char *name;
+	dev_handle_t dev;
+	uint64_t size_mb;
+
+	rc = devmap_device_get_handle(dev_name, &indev_handle, 0);
+	if (rc != EOK) {
+		printf(NAME ": could not resolve device `%s'.\n", dev_name);
+		return rc;
+	}
+
+	rc = block_init(indev_handle, 2048);
+	if (rc != EOK)  {
+		printf(NAME ": could not init libblock.\n");
+		return rc;
+	}
+
+	/* Determine and verify block size. */
+
+	rc = block_get_bsize(indev_handle, &block_size);
+	if (rc != EOK) {
+		printf(NAME ": error getting block size.\n");
+		return rc;
+	}
+
+	if (block_size < 512 || (block_size % 512) != 0) {
+		printf(NAME ": invalid block size %d.\n");
+		return ENOTSUP;
+	}
+
+	/* Read in partition records. */
+	rc = mbr_part_read();
+	if (rc != EOK)
+		return rc;
+
+	/* Register the driver with device mapper. */
+	rc = devmap_driver_register(NAME, mbr_connection);
+	if (rc != EOK) {
+		printf(NAME ": Unable to register driver.\n");
+		return rc;
+	}
+
+	/* Create partition devices. */
+	for (i = 0; i < N_PRIMARY; ++i) {
+		/* Skip absent partitions. */
+		if (!primary[i].present)
+			continue;
+
+		asprintf(&name, "%sp%d", dev_name, i);
+		rc = devmap_device_register(name, &dev);
+		if (rc != EOK) {
+			devmap_hangup_phone(DEVMAP_DRIVER);
+			printf(NAME ": Unable to register device %s.\n", name);
+			return rc;
+		}
+
+		size_mb = (primary[i].length * block_size + 1024 * 1024 - 1)
+		    / (1024 * 1024);
+		printf(NAME ": Registered device %s: %llu blocks %llu MB.\n",
+		    name, primary[i].length, size_mb);
+
+		primary[i].dev = dev;
+	}
+
+	return EOK;
+}
+
+/** Read in partition records. */
+static int mbr_part_read(void)
+{
+	int i, rc;
+	br_block_t *mbr;
+	uint16_t sgn;
+	uint32_t sa, len;
+
+	rc = block_bb_read(indev_handle, 0);
+	if (rc != EOK) {
+		printf(NAME ": Failed reading MBR block.\n");
+		return rc;
+	}
+
+	mbr = block_bb_get(indev_handle);
+	sgn = uint16_t_le2host(mbr->signature);
+
+	if (sgn != BR_SIGNATURE) {
+		printf(NAME ": Invalid boot record signature 0x%04X.\n", sgn);
+		return EINVAL;
+	}
+
+	for (i = 0; i < N_PRIMARY; ++i) {
+		sa  = uint32_t_le2host(mbr->pte[i].first_lba);
+		len = uint32_t_le2host(mbr->pte[i].length);
+
+		primary[i].start_addr = sa;
+		primary[i].length = len;
+
+		primary[i].present = (sa != 0 || len != 0) ? true : false;
+		primary[i].dev = 0;
+	}
+}
+
+static void mbr_connection(ipc_callid_t iid, ipc_call_t *icall)
+{
+	size_t comm_size;
+	void *fs_va = NULL;
+	ipc_callid_t callid;
+	ipc_call_t call;
+	ipcarg_t method;
+	dev_handle_t dh;
+	int flags;
+	int retval;
+	uint64_t ba;
+	size_t cnt;
+	int pidx, i;
+	part_t *part;
+
+	/* Get the device handle. */
+	dh = IPC_GET_ARG1(*icall);
+
+	/* Determine which partition device is the client connecting to. */
+	pidx = -1;
+	for (i = 0; i < N_PRIMARY; i++)
+		if (primary[i].dev == dh)
+			pidx = i;
+
+	if (pidx < 0/* || disk[disk_id].present == false*/) {
+		ipc_answer_0(iid, EINVAL);
+		return;
+	}
+
+	part = &primary[pidx];
+
+	/* Answer the IPC_M_CONNECT_ME_TO call. */
+	ipc_answer_0(iid, EOK);
+
+	if (!ipc_share_out_receive(&callid, &comm_size, &flags)) {
+		ipc_answer_0(callid, EHANGUP);
+		return;
+	}
+
+	fs_va = as_get_mappable_page(comm_size);
+	if (fs_va == NULL) {
+		ipc_answer_0(callid, EHANGUP);
+		return;
+	}
+
+	(void) ipc_share_out_finalize(callid, fs_va);
+
+	while (1) {
+		callid = async_get_call(&call);
+		method = IPC_GET_METHOD(call);
+		switch (method) {
+		case IPC_M_PHONE_HUNGUP:
+			/* The other side has hung up. */
+			ipc_answer_0(callid, EOK);
+			return;
+		case BD_READ_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
+				retval = ELIMIT;
+				break;
+			}
+			retval = mbr_bd_read(part, ba, cnt, fs_va);
+			break;
+		case BD_WRITE_BLOCKS:
+			ba = MERGE_LOUP32(IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			cnt = IPC_GET_ARG3(call);
+			if (cnt * block_size > comm_size) {
+				retval = ELIMIT;
+				break;
+			}
+			retval = mbr_bd_write(part, ba, cnt, fs_va);
+			break;
+		case BD_GET_BLOCK_SIZE:
+			ipc_answer_1(callid, EOK, block_size);
+			continue;
+
+		default:
+			retval = EINVAL;
+			break;
+		}
+		ipc_answer_0(callid, retval);
+	}
+}
+
+/** Read blocks from partition. */
+static int mbr_bd_read(part_t *p, uint64_t ba, size_t cnt, void *buf)
+{
+	uint64_t gba;
+
+	if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
+		return ELIMIT;
+
+	return block_read_direct(indev_handle, gba, cnt, buf);
+}
+
+/** Write blocks to partition. */
+static int mbr_bd_write(part_t *p, uint64_t ba, size_t cnt, const void *buf)
+{
+	uint64_t gba;
+
+	if (mbr_bsa_translate(p, ba, cnt, &gba) != EOK)
+		return ELIMIT;
+
+	return block_write_direct(indev_handle, gba, cnt, buf);
+}
+
+/** Translate block segment address with range checking. */
+static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba)
+{
+	if (ba + cnt > p->length)
+		return ELIMIT;
+
+	*gba = p->start_addr + ba;
+	return EOK;
+}
+
+/**
+ * @}
+ */
