diff --git a/arch/sparc/Kbuild b/arch/sparc/Kbuild
index ae589c82c947839070a2580a6a2bf4806562b33b..082d329d32457042a5519ea71ba36ef3b1b579b6 100644
--- a/arch/sparc/Kbuild
+++ b/arch/sparc/Kbuild
@@ -1 +1,2 @@
 obj-y += kernel/
+obj-y += mm/
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 9a0a4aa017699bacd0400439d2e3d6db8b92fd25..5371255217e7ed8ff1cab5e1a26bf20bf98ba261 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -6,8 +6,9 @@ config PAGE_OFFSET
 	bool "Use an offset in physical/virtual page address conversion"
 	default n
 	help
-	  Use a fixed offset when computing the virtual and physical page
-	  addresses.
+	  Use an offset when computing the virtual and physical page
+	  addresses. This can not work unless the kernel is bootstrapped.
+	  If unsure, say N.
 
 config EXTRA_SPARC_PHYS_BANKS
 	int "Number of extra physical memory banks"
@@ -19,6 +20,7 @@ config EXTRA_SPARC_PHYS_BANKS
 
 menu "Memory Management Settings"
 	depends on MM
+	depends on CHUNK
 
 config SPARC_MM_BLOCK_ORDER_MAX
 	int "Initial memory block order upper limit"
@@ -50,6 +52,31 @@ config SPARC_INIT_PAGE_MAP_MAX_ENTRIES
 	  Configures the storage space for the initial page map. It's wise to
 	  say at least EXTRA_SPARC_PHYS_BANKS here.
 
+config SPARC_BOOTMEM_RESERVE_CHUNK_ORDER
+	int "Memory block order to reserve for boot memory allocator"
+	depends on MM
+	depends on CHUNK
+	default 20
+	range  12 SPARC_MM_BLOCK_ORDER_MAX
+	help
+	  The order (i.e. 2^N bytes) of memory to reserve for the boot memory
+	  allocator in a single request to the higher-tier memory manager.
+	  The boot memory allocator is used by low level functionality, such as
+	  the mangement of MMU translation tables, so choose a large enough
+	  number. The default (20, i.e. 1 MiB) is a good starting point.
+	  Allowed order range is 12 (SPARC page size) to
+	  SPARC_MM_BLOCK_ORDER_MAX.
+
+config SPARC_BOOTMEM_REQUEST_NEW_ON_DEMAND
+	bool "Allow the boot memory allocator to request new memory blocks"
+	depends on MM
+	depends on CHUNK
+	default y
+	help
+	 Allow the boot memory allocator to request new blocks of size
+	 SPARC_BOOTMEM_RESERVE_CHUNK_ORDER from the higher-tier memory manager.
+	 Beware that this will potentially lead to greater memory fragmentation.
+	 If unsure, say Y.
 endmenu
 
 
diff --git a/arch/sparc/include/asm/io.h b/arch/sparc/include/asm/io.h
new file mode 100644
index 0000000000000000000000000000000000000000..f4afa6edaf5816ff22ad0a5793ca0f209f462865
--- /dev/null
+++ b/arch/sparc/include/asm/io.h
@@ -0,0 +1,117 @@
+/**
+ * @file    sparc/include/asm/io.h
+ *
+ * @copyright GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief a collection of accessor functions and macros to perform unbuffered
+ *        access to memory or hardware registers
+ */
+
+#ifndef _ARCH_SPARC_ASM_IO_H_
+#define _ARCH_SPARC_ASM_IO_H_
+
+#include <stdint.h>
+#include <uapi/asi.h>
+
+/*
+ * convention/calls same as in linux kernel (see arch/sparc/include/asm/io_32.h)
+ */
+
+
+
+#define __raw_readb __raw_readb
+static inline uint8_t __raw_readb(const volatile void *addr)
+{
+	uint8_t ret;
+
+	__asm__ __volatile__("lduba	[%1] %2, %0\n\t"
+			     : "=r" (ret)
+			     : "r" (addr), "i" (ASI_LEON_NOCACHE));
+
+	return ret;
+}
+
+#define __raw_readw __raw_readw
+static inline uint16_t __raw_readw(const volatile void *addr)
+{
+	uint16_t ret;
+
+	__asm__ __volatile__("lduha	[%1] %2, %0\n\t"
+			     : "=r" (ret)
+			     : "r" (addr), "i" (ASI_LEON_NOCACHE));
+
+	return ret;
+}
+
+
+#define __raw_readl __raw_readl
+static inline uint32_t __raw_readl(const volatile void *addr)
+{
+	uint32_t ret;
+
+	__asm__ __volatile__("lda	[%1] %2, %0\n\t"
+			     : "=r" (ret)
+			     : "r" (addr), "i" (ASI_LEON_NOCACHE));
+
+	return ret;
+}
+
+#define __raw_writeb __raw_writeb
+static inline void __raw_writeb(uint8_t w, const volatile void *addr)
+{
+	__asm__ __volatile__("stba	%r0, [%1] %2\n\t"
+			     :
+			     : "Jr" (w), "r" (addr), "i" (ASI_LEON_NOCACHE));
+}
+
+#define __raw_writew __raw_writew
+static inline void __raw_writew(uint16_t w, const volatile void *addr)
+{
+	__asm__ __volatile__("stha	%r0, [%1] %2\n\t"
+			     :
+			     : "Jr" (w), "r" (addr), "i" (ASI_LEON_NOCACHE));
+}
+
+
+#define __raw_writel __raw_writel
+static inline void __raw_writel(uint32_t l, const volatile void *addr)
+{
+	__asm__ __volatile__("sta	%r0, [%1] %2\n\t"
+			     :
+			     : "Jr" (l), "r" (addr), "i" (ASI_LEON_NOCACHE));
+}
+
+#ifndef ioread8
+#define ioread8(X)                      __raw_read8(X)
+#endif
+
+#ifndef iowrite8
+#define iowrite8(X)                     __raw_write8(X)
+#endif
+
+#ifndef ioread16be
+#define ioread16be(X)                   __raw_readw(X)
+#endif
+
+#ifndef ioread32be
+#define ioread32be(X)                   __raw_readl(X)
+#endif
+
+#ifndef iowrite16be
+#define iowrite16be(val,X)              __raw_writew(val,X)
+#endif
+
+#ifndef iowrite32be
+#define iowrite32be(val,X)              __raw_writel(val,X)
+#endif
+
+#endif /* _ARCH_SPARC_ASM_IO_H_ */
diff --git a/arch/sparc/include/asm/leon.h b/arch/sparc/include/asm/leon.h
new file mode 100644
index 0000000000000000000000000000000000000000..8737d4137ffed6098fa12c9c2d4ed83e1b40e85f
--- /dev/null
+++ b/arch/sparc/include/asm/leon.h
@@ -0,0 +1,205 @@
+/**
+ * @file asm/leon.h
+ *
+ * @author  Armin Luntzer (armin.luntzer@univie.ac.at)
+ * @date    2015
+ *
+ * @copyright GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief assembly functions for the leon3 target
+ *
+ */
+
+#ifndef _SPARC_ASM_LEON_H_
+#define _SPARC_ASM_LEON_H_
+
+
+
+#define ASI_LEON3_SYSCTRL	0x02
+
+#define ASI_LEON3_SYSCTRL_CCR	0x00
+#define ASI_LEON3_SYSCTRL_ICFG	0x08
+#define ASI_LEON3_SYSCTRL_DCFG	0x0c
+
+
+
+__attribute__((unused))
+static inline unsigned long leon3_asr17(void)
+{
+	unsigned long asr17;
+
+	__asm__ __volatile__ (
+			"rd	%%asr17, %0	\n\t"
+			:"=r" (asr17)
+			);
+
+	return asr17;
+}
+
+__attribute__((unused))
+static inline unsigned long leon3_cpuid(void)
+{
+	unsigned long cpuid;
+
+	__asm__ __volatile__ (
+			"rd	%%asr17, %0	\n\t"
+			"srl	%0, 28, %0	\n\t"
+			:"=r" (cpuid)
+			:
+			:"l1");
+        return cpuid;
+}
+
+__attribute__((unused))
+static inline void leon3_powerdown_safe(unsigned long phys_memaddr)
+{
+	__asm__ __volatile__(
+			"wr	%%g0, %%asr19	\n\t"
+			"lda	[%0] 0x1c, %%g0	\n\t"
+			:
+			:"r" (phys_memaddr)
+			:"memory");
+}
+
+__attribute__((unused))
+static inline void leon3_flush(void)
+{
+	__asm__ __volatile__(
+			"flush			\n\t"
+			"set	0x81000f, %%g1	\n\t"
+			"sta	%%g1, [%0] %1	\n\t"
+			:
+			: "r" (ASI_LEON3_SYSCTRL_CCR),
+			  "i" (ASI_LEON3_SYSCTRL)
+			: "g1");
+}
+
+__attribute__((unused))
+static inline void leon3_enable_icache(void)
+{
+	__asm__ __volatile__(
+			"lda	[%0] %1, %%l1		\n\t"
+			"set	0x3,     %%l2		\n\t"
+			"or	%%l2,	  %%l1,	%%l2	\n\t"
+			"sta	%%l2, [%0] %1		\n\t"
+			:
+			: "r" (ASI_LEON3_SYSCTRL_CCR),
+			  "i" (ASI_LEON3_SYSCTRL)
+			: "l1", "l2");
+}
+
+__attribute__((unused))
+static inline void leon3_enable_dcache(void)
+{
+	__asm__ __volatile__(
+			"lda	[%0] %1, %%l1		\n\t"
+			"set	0xc,     %%l2		\n\t"
+			"or	%%l2,	  %%l1,	%%l2	\n\t"
+			"sta	%%l2, [%0] %1		\n\t"
+			:
+			: "r" (ASI_LEON3_SYSCTRL_CCR),
+			  "i" (ASI_LEON3_SYSCTRL)
+			: "l1", "l2");
+}
+
+
+__attribute__((unused))
+static inline void leon3_enable_snooping(void)
+{
+	__asm__ __volatile__(
+			"lda	[%0] %1, %%l1		\n\t"
+			"set	0x800000, %%l2		\n\t"
+			"or	%%l2,	  %%l1,	%%l2	\n\t"
+			"sta	%%l2, [%0] %1		\n\t"
+			:
+			: "r" (ASI_LEON3_SYSCTRL_CCR),
+			  "i" (ASI_LEON3_SYSCTRL)
+			: "l1", "l2");
+}
+
+__attribute__((unused))
+static inline void leon3_enable_fault_tolerant(void)
+{
+	__asm__ __volatile__(
+			"lda	[%0] %1, %%l1		\n\t"
+			"set	0x80000, %%l2		\n\t"
+			"or	%%l2,	  %%l1,	%%l2	\n\t"
+			"sta	%%l2, [%0] %1		\n\t"
+			:
+			: "r" (ASI_LEON3_SYSCTRL_CCR),
+			  "i" (ASI_LEON3_SYSCTRL)
+			: "l1", "l2");
+}
+
+
+
+
+__attribute__((unused))
+static inline void leon_set_sp(unsigned long stack_addr)
+{
+	__asm__ __volatile__(
+			"mov %0, %%sp\n\t"
+			:
+			:"r"(stack_addr)
+			:"memory");
+}
+
+__attribute__((unused))
+static inline void leon_set_fp(unsigned long stack_addr)
+{
+	__asm__ __volatile__(
+			"mov %0, %%fp\n\t"
+			:
+			:"r" (stack_addr)
+			:"memory");
+}
+
+
+__attribute__((unused))
+static inline unsigned long leon_get_sp(void)
+{
+	unsigned long sp;
+
+	__asm__ __volatile__ (
+			"mov	%%sp, %0 \n\t"
+			:"=r" (sp)
+			);
+
+	return sp;
+}
+
+
+__attribute__((unused))
+static inline unsigned long leon_get_fp(void)
+{
+	unsigned long fp;
+
+	__asm__ __volatile__ (
+			"mov	%%fp, %0 \n\t"
+			:"=r" (fp)
+			);
+
+	return fp;
+}
+
+
+/* XXX need to make sure this trap is installed */ 
+__attribute__((unused))
+static inline void leon_reg_win_flush(void)
+{
+	__asm__ __volatile__("ta 3");
+}
+
+
+
+
+#endif /* _SPARC_ASM_LEON_H_ */
diff --git a/arch/sparc/include/cpu_type.h b/arch/sparc/include/cpu_type.h
new file mode 100644
index 0000000000000000000000000000000000000000..9e963b7cb94d2bf430309d7bc483c74ba6a05f5a
--- /dev/null
+++ b/arch/sparc/include/cpu_type.h
@@ -0,0 +1,21 @@
+/**
+ * @brief supported CPU models
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ */
+
+#ifndef _ASM_CPU_TYPE_H_
+#define _ASM_CPU_TYPE_H_
+
+
+/*
+ * supported CPU models
+ */
+
+enum sparc_cpus {
+	sparc_leon  = 0x06,
+};
+
+extern enum sparc_cpus sparc_cpu_model;
+
+
+#endif /* _ASM_CPU_TYPE_H_ */
diff --git a/arch/sparc/include/init.h b/arch/sparc/include/init.h
index 41383638ce4a7191bf0f2ddc1295b7993213cf49..4f64dd825322bb5c539460b741372429bded5a10 100644
--- a/arch/sparc/include/init.h
+++ b/arch/sparc/include/init.h
@@ -2,8 +2,16 @@
  * @file arch/sparc/include/init.h
  */
 
-#ifndef _SPARC_INIT_H_ 
-#define _SPARC_INIT_H_ 
+#ifndef _SPARC_INIT_H_
+#define _SPARC_INIT_H_
+
+#if defined(CONFIG_KERNEL_STACK_PAGES)
+#define KERNEL_STACK_PAGES CONFIG_KERNEL_STACK_PAGES
+#else
+#define KERNEL_STACK_PAGES 8
+#endif
+
+
 
 void paging_init(void);
 
diff --git a/arch/sparc/include/mm.h b/arch/sparc/include/mm.h
index 1fa7d116e2ec48bdcd0e7e92bfea57fd8c510f36..5cac7dbc2ccfedc1610552a3eb144d95bbcaae84 100644
--- a/arch/sparc/include/mm.h
+++ b/arch/sparc/include/mm.h
@@ -72,6 +72,18 @@ extern struct list_head mm_init_block_order[MM_BLOCK_ORDER_MAX + 1];
 
 
 
+#define BOOTMEM_CHUNKSIZE (1 << CONFIG_SPARC_BOOTMEM_RESERVE_CHUNK_ORDER)
+
+
 void bootmem_init(void);
+void *bootmem_alloc(size_t size);
+void bootmem_free(void *ptr);
+
+void mm_mmu_paging_init(void);
+
+void mm_mmu_trap(void);
+
+int mm_set_mmu_ctx(unsigned long ctx);
+unsigned long mm_get_mmu_ctx(void);
 
 #endif /*_SPARC_MM_H_ */
diff --git a/arch/sparc/include/page.h b/arch/sparc/include/page.h
index 255bff4e2d4de847c98178d18c27975c952f94a6..f52411502cbf59fce35a7f3e88edc2be51a51ec9 100644
--- a/arch/sparc/include/page.h
+++ b/arch/sparc/include/page.h
@@ -20,14 +20,41 @@
 
 /* align address to the (next) page boundary */
 #define PAGE_ALIGN(addr)	ALIGN(addr, PAGE_SIZE)
+#define PAGE_ALIGN_PTR(addr)	PTR_ALIGN(addr, PAGE_SIZE)
 
 #define PFN_PHYS(x)	((unsigned long)((x) << PAGE_SHIFT))
-#define PHYS_PFN(x)	((unsigned long)((x) >> PAGE_SHIFT))
+#define PHYS_PFN(x)((unsigned long)((x) >> PAGE_SHIFT))
 
 
-#if defined(CONFIG_PAGE_OFFSET)
-#define PAGE_OFFSET	0xf0000000
-#endif
+
+/**
+ * Reserved memory ranges used in mappings:
+ *
+ *  - everything above HIGHMEM_START is 1:1 mapped through the MMU and marks
+ *    the per-thread allocatable memory.
+ *  - LOWMEM_RESERVED is the reserved lower address range
+ *  - VMALLOC_{START, END} define the boundaries of mapped virtual pages
+ *
+ * TODO: make this configurable via make config
+ * The values are selected such that:
+ *
+ *	 1. the lowest 16M are reserved
+ *	 2. highmem starts at 0x40000000, this is typically the lowest (RAM)
+ *	    address in use on a LEON platform (usually internal static RAM).
+ *	 3. the above boundaries are selected under the condition that the
+ *	    kernel is not bootstrapped, i.e. an initial MMU context is not set
+ *	    up by a stage 1 loader, which then copies and starts the kernel
+ *	    from some location.
+ */
+
+#define HIGHMEM_START	0x40000000
+#define LOWMEM_RESERVED 0x01000000
+
+#define VMALLOC_START	(LOWMEM_RESERVED)
+#define VMALLOC_END	(HIGHMEM_START - 1)
+
+
+#define PAGE_OFFSET	HIGHMEM_START
 
 extern unsigned long phys_base;
 extern unsigned long pfn_base;
@@ -44,7 +71,7 @@ extern unsigned long pfn_base;
 #if defined (CONFIG_SPARC_INIT_PAGE_MAP_MAX_ENTRIES)
 #define INIT_PAGE_MAP_MAX_ENTRIES CONFIG_SPARC_INIT_PAGE_MAP_MAX_ENTRIES
 #else
-#define INIT_PAGE_MAP_MAX_ENTRIES 1
+#define INIT_PAGE_MAP_MAX_ENTRIES 0
 #endif
 
 extern struct mm_pool  mm_init_page_pool;
diff --git a/arch/sparc/include/srmmu.h b/arch/sparc/include/srmmu.h
new file mode 100644
index 0000000000000000000000000000000000000000..32683574a2ba060ccb99cb13934f0a7667923977
--- /dev/null
+++ b/arch/sparc/include/srmmu.h
@@ -0,0 +1,245 @@
+/**
+ * @brief SPARC Reference (SR) Memory Management Unit (MMU)
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ *
+ * @see SPARCv8 Architecture Manual for more info
+ */
+
+#ifndef _SPARC_SRMMU_H_
+#define _SPARC_SRMMU_H_
+
+
+#include <stddef.h>
+
+
+/* XXX: not here */
+#define __BIG_ENDIAN_BITFIELD __BIG_ENDIAN_BITFIELD
+
+
+#define SRMMU_CTRL_IMPL_MASK	0xf0000000
+#define SRMMU_CTRL_VER_MASK	0x0f000000
+
+#define SRMMU_CTRL_IMPL_SHIFT		 28
+#define SRMMU_CTRL_VER_SHIFT		 24
+
+
+#define SRMMU_CONTEXTS			256
+#define SRMMU_SIZE_TBL_LVL_1		256
+#define SRMMU_SIZE_TBL_LVL_2		 64
+#define SRMMU_SIZE_TBL_LVL_3		 64
+#define SRMMU_ENTRY_SIZE_BYTES		  4
+
+/* SRMMU page tables must be aligned to their size */
+#define SRMMU_TBL_LVL_1_ALIGN (SRMMU_SIZE_TBL_LVL_1 * SRMMU_ENTRY_SIZE_BYTES)
+#define SRMMU_TBL_LVL_2_ALIGN (SRMMU_SIZE_TBL_LVL_2 * SRMMU_ENTRY_SIZE_BYTES)
+#define SRMMU_TBL_LVL_3_ALIGN (SRMMU_SIZE_TBL_LVL_3 * SRMMU_ENTRY_SIZE_BYTES)
+
+#define SRMMU_SMALL_PAGE_SIZE	PAGE_SIZE
+#define SRMMU_MEDIUM_PAGE_SIZE	(SRMMU_SMALL_PAGE_SIZE  * SRMMU_SIZE_TBL_LVL_3)
+#define SRMMU_LARGE_PAGE_SIZE	(SRMMU_MEDIUM_PAGE_SIZE * SRMMU_SIZE_TBL_LVL_2)
+
+
+/* full 32 bit range divided by 256 lvl1 contexts:
+ * upper 8 bits == 16 MiB pages
+ *
+ * a 16 MiB page divided by 64 lvl2 contexts:
+ * next 6 bits = 256 kiB pages
+ *
+ * a 256 kiB page divided by 64 lvl3 contexts:
+ * next 6 bits = 4 kiB pages
+ *
+ * the last 12 bits are the offset within a 4096 kiB page
+ */
+#define SRMMU_TBL_LVL_1_SHIFT		24
+#define SRMMU_TBL_LVL_1_MASK		0xff
+#define SRMMU_TBL_LVL_2_SHIFT		18
+#define SRMMU_TBL_LVL_2_MASK		0x3f
+#define SRMMU_TBL_LVL_3_SHIFT		12
+#define SRMMU_TBL_LVL_3_MASK		0x3f
+
+/* offsets into the different level tables */
+#define SRMMU_LVL1_GET_TBL_OFFSET(addr)	((addr >> SRMMU_TBL_LVL_1_SHIFT) \
+					 & SRMMU_TBL_LVL_1_MASK)
+#define SRMMU_LVL2_GET_TBL_OFFSET(addr)	((addr >> SRMMU_TBL_LVL_2_SHIFT) \
+					 & SRMMU_TBL_LVL_2_MASK)
+#define SRMMU_LVL3_GET_TBL_OFFSET(addr)	((addr >> SRMMU_TBL_LVL_3_SHIFT) \
+					 & SRMMU_TBL_LVL_3_MASK)
+
+
+#define SRMMU_ENTRY_TYPE_INVALID	0x0
+#define SRMMU_ENTRY_TYPE_PT_DESC	0x1
+#define SRMMU_ENTRY_TYPE_PT_ENTRY	0x2
+#define SRMMU_ENTRY_TYPE_RESERVED	0x3
+#define SRMMU_ENTRY_TYPE_MASK		0x3
+
+#define SRMMU_PTE_FLAGS_MASK		0xff
+
+#define SRMMU_CACHEABLE			0x80
+
+#define SRMMMU_ACC_SHIFT	   2
+
+/* user access permissions, ASI 0x8 or 0xa */
+#define SRMMU_ACC_U_R_1		(0x0 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_U_RW		(0x1 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_U_RX		(0x2 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_U_RWX		(0x3 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_U_X		(0x4 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_U_R_2		(0x5 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_U_NO_ACCESS_1	(0x6 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_U_NO_ACCESS_2	(0x7 << SRMMMU_ACC_SHIFT)
+
+/* supervisor access permissions, ASI 0x9 or 0xb */
+#define SRMMU_ACC_S_R		(0x0 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_S_RW_1	(0x1 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_S_RX_1	(0x2 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_S_RWX_1	(0x3 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_S_X		(0x4 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_S_RW_2	(0x5 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_S_RX_2	(0x6 << SRMMMU_ACC_SHIFT)
+#define SRMMU_ACC_S_RWX_2	(0x7 << SRMMMU_ACC_SHIFT)
+
+
+/**
+ * The physical address is 36 bits long and composed of a page offset that is 
+ * 12 bits wide and the physical page number, that is 24 bits wide
+ * The virtual address is composed of the virtual page number, which is 20
+ * bits wide and the page offset that is the same as in the physical address.
+ * The 4 extra bits are used to map 4 GB sections. Any address that is used
+ * to set up the page table entry/descriptor address is hence shifted right
+ * by 4 bits for the correct representation of the physical page number
+ */
+#define SRMMU_PTD(addr)	(((addr >> 4) & ~SRMMU_ENTRY_TYPE_MASK) | \
+			SRMMU_ENTRY_TYPE_PT_DESC)
+
+#define SRMMU_PTE(addr, flags)	(((addr >> 4) & ~SRMMU_PTE_FLAGS_MASK) | \
+			SRMMU_ENTRY_TYPE_PT_ENTRY | flags)
+
+#define SRMMU_PTD_TO_ADDR(ptd) ((ptd & ~SRMMU_ENTRY_TYPE_MASK) << 4)
+
+#define SRMMU_PTE_TO_ADDR(pte) ((pte & ~(SRMMU_PTE_FLAGS_MASK)) << 4)
+
+/**
+ * a SRMMU page table descriptor/entry
+ * the page table pointer must be aligned to a boundary equal to the size of
+ * the next level page table, i.e.
+ * SRMMU_SIZE_TBL_LVL_1 * SRMMU_ENTRY_SIZE_BYTES for level 1,
+ * SRMMU_SIZE_TBL_LVL_2 * SRMMU_ENTRY_SIZE_BYTES for level 2,
+ * SRMMU_SIZE_TBL_LVL_3 * SRMMU_ENTRY_SIZE_BYTES for level 3.
+ *
+ * The entry type field may be set to INVALID, PT_DESC, PT_ENTRY or RESERVED
+ * and determines whether the MMU treats the remaining bits as page table
+ * descriptor or entry
+ *
+ * @note the cacheable flag should be 0 for all mapped i/o locations
+ */
+__extension__
+struct srmmu_ptde {
+	union {
+#if defined(__BIG_ENDIAN_BITFIELD)
+		struct {
+			unsigned long page_table_pointer:30;
+			unsigned long entry_type : 2;	
+		};
+
+		struct {
+			unsigned long physical_page_number :24;
+			unsigned long cacheable		   : 1;	
+			unsigned long modified		   : 1;	
+			unsigned long referenced	   : 1;	
+			unsigned long access_permissions   : 3;	
+			unsigned long entry_type	   : 2;	
+		};
+#endif
+		unsigned long ptd;
+		unsigned long pte;
+	};
+};
+
+
+/* page table level the fault occured in */
+#define SRMMU_FLT_L_CTX		0
+#define SRMMU_FLT_L_LVL1	1
+#define SRMMU_FLT_L_LVL2	2
+#define SRMMU_FLT_L_LVL3	3
+
+
+/* srmmu access type faults (User, Superuser, Read, Write eXecute */
+#define SRMMU_FLT_AT_R__U_DATA	0
+#define SRMMU_FLT_AT_R__S_DATA	1
+#define SRMMU_FLT_AT_RX_U_INST	2
+#define SRMMU_FLT_AT_RX_S_INST	3
+#define SRMMU_FLT_AT_W__U_DATA	4
+#define SRMMU_FLT_AT_W__S_DATA	5
+#define SRMMU_FLT_AT_W__U_INST	6
+#define SRMMU_FLT_AT_W__S_INST	7
+
+
+/* fault type */
+#define SRMMU_FLT_FT_NONE		0
+#define SRMMU_FLT_FT_INVALID_ADDR	1
+#define SRMMU_FLT_FT_PROTECTION		2
+#define SRMMU_FLT_FT_PRIV_VIOLATION	3
+#define SRMMU_FLT_FT_TRANSLATION	4
+#define SRMMU_FLT_FT_ACCESS_BUS		5
+#define SRMMU_FLT_FT_INTERNAL		6
+
+
+#define SRMMU_FLT_EBE_SHIFT	10	/* external bus error (impl. dep.) */
+#define SRMMU_FLT_L_SHIFT	8	/* page table level */
+#define SRMMU_FLT_AT_SHIFT	5	/* access type */
+#define SRMMU_FLT_FT_SHIFT	2	/* fault type */
+#define SRMMU_FLT_FAV_SHIFT	1	/* fault address valid */
+#define SRMMU_FLT_OV_SHIFT	0	/* multiple faults occured since read */
+
+
+
+/* srmmu fault status register, see SPARCv8 manual, p256 */
+__extension__
+struct srmmu_fault_status {
+	union {
+#if defined(__BIG_ENDIAN_BITFIELD)
+		struct {
+			unsigned long reserved:14;
+			unsigned long external_bus_error:8;
+			unsigned long page_table_level:2;
+			unsigned long access_type:3;
+			unsigned long fault_type:3;	
+			unsigned long fault_address_vaild:1;
+			unsigned long overwrite:1;
+		};
+#endif
+		unsigned long status;
+	};
+};
+
+
+
+
+int srmmu_select_ctx(unsigned long ctx_num);
+void srmmu_enable_mmu(void);
+
+int srmmu_init(void *(*alloc)(size_t size), void (*free)(void *addr));
+
+int srmmu_do_small_mapping_range(unsigned long ctx_num,
+				 unsigned long va, unsigned long pa,
+				 unsigned long num_pages, unsigned long perm);
+
+int srmmu_do_large_mapping_range(unsigned long ctx_num,
+				 unsigned long va, unsigned long pa,
+				 unsigned long num_pages, unsigned long perm);
+
+int srmmu_do_small_mapping(unsigned long ctx_num,
+			   unsigned long va, unsigned long pa,
+			   unsigned long perm);
+
+
+int srmmu_do_large_mapping(unsigned long ctx_num,
+			   unsigned long va, unsigned long pa,
+			   unsigned long perm);
+
+
+void srmmu_release_pages(unsigned long ctx_num,
+			 unsigned long va, unsigned long va_end,
+			 void  (*free_page)(void *addr));
+
+#endif /*_SPARC_SRMMU_H_ */
diff --git a/arch/sparc/include/srmmu_access.h b/arch/sparc/include/srmmu_access.h
new file mode 100644
index 0000000000000000000000000000000000000000..6dca38375327846380d08ed49d0bf20da2ff62ba
--- /dev/null
+++ b/arch/sparc/include/srmmu_access.h
@@ -0,0 +1,27 @@
+/**
+ * @brief SPARC Reference (SR) Memory Management Unit (MMU) access functions
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ *
+ * @see SPARCv8 Architecture Manual for more info
+ */
+
+#ifndef _SPARC_SRMMU_ACCESS_H_
+#define _SPARC_SRMMU_ACCESS_H_
+
+unsigned int srmmu_get_mmu_ctrl(void);
+struct srmmu_fault_status srmmu_get_mmu_fault_status(void);
+unsigned int srmmu_get_mmu_fault_address(void);
+unsigned int srmmu_get_mmu_impl(void);
+unsigned int srmmu_get_mmu_ver(void);
+
+
+void srmmu_set_ctx_tbl_addr(unsigned long addr);
+void srmmu_set_ctx(unsigned int ctx);
+
+void leon_flush_cache_all(void);
+void leon_flush_tlb_all(void);
+void srmmu_set_mmureg(unsigned long regval);
+unsigned int srmmu_get_mmureg(void);
+
+
+#endif /*_SPARC_SRMMU_ACCESS_H_ */
diff --git a/arch/sparc/include/stack.h b/arch/sparc/include/stack.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f9c76e34654d97b4c091d8e921462af34c91991
--- /dev/null
+++ b/arch/sparc/include/stack.h
@@ -0,0 +1,131 @@
+#ifndef _SPARC_STACK_H_
+#define _SPARC_STACK_H_
+
+#include <stdint.h>
+
+/* reg window offset */
+#define RW_L0     0x00
+#define RW_L1     0x04
+#define RW_L2     0x08
+#define RW_L3     0x0c
+#define RW_L4     0x10
+#define RW_L5     0x14
+#define RW_L6     0x18
+#define RW_L7     0x1c
+#define RW_I0     0x20
+#define RW_I1     0x24
+#define RW_I2     0x28
+#define RW_I3     0x2c
+#define RW_I4     0x30
+#define RW_I5     0x34
+#define RW_I6     0x38
+#define RW_I7     0x3c
+
+/* stack frame offsets */
+#define SF_L0     0x00
+#define SF_L1     0x04
+#define SF_L2     0x08
+#define SF_L3     0x0c
+#define SF_L4     0x10
+#define SF_L5     0x14
+#define SF_L6     0x18
+#define SF_L7     0x1c
+#define SF_I0     0x20
+#define SF_I1     0x24
+#define SF_I2     0x28
+#define SF_I3     0x2c
+#define SF_I4     0x30
+#define SF_I5     0x34
+#define SF_FP     0x38
+#define SF_PC     0x3c
+#define SF_RETP   0x40
+#define SF_XARG0  0x44
+#define SF_XARG1  0x48
+#define SF_XARG2  0x4c
+#define SF_XARG3  0x50
+#define SF_XARG4  0x54
+#define SF_XARG5  0x58
+#define SF_XXARG  0x5c
+
+#define UREG_G0        0
+#define UREG_G1        1
+#define UREG_G2        2
+#define UREG_G3        3
+#define UREG_G4        4
+#define UREG_G5        5
+#define UREG_G6        6
+#define UREG_G7        7
+#define UREG_I0        8
+#define UREG_I1        9
+#define UREG_I2        10
+#define UREG_I3        11
+#define UREG_I4        12
+#define UREG_I5        13
+#define UREG_I6        14
+#define UREG_I7        15
+#define UREG_FP        UREG_I6
+#define UREG_RETPC     UREG_I7
+
+/* These for pt_regs. */
+#define PT_PSR    0x0
+#define PT_PC     0x4
+#define PT_NPC    0x8
+#define PT_Y      0xc
+#define PT_G0     0x10
+#define PT_WIM    PT_G0
+#define PT_G1     0x14
+#define PT_G2     0x18
+#define PT_G3     0x1c
+#define PT_G4     0x20
+#define PT_G5     0x24
+#define PT_G6     0x28
+#define PT_G7     0x2c
+#define PT_I0     0x30
+#define PT_I1     0x34
+#define PT_I2     0x38
+#define PT_I3     0x3c
+#define PT_I4     0x40
+#define PT_I5     0x44
+#define PT_I6     0x48
+#define PT_FP     PT_I6
+#define PT_I7     0x4c
+
+
+
+
+
+struct pt_regs {
+        uint32_t psr;
+        uint32_t pc;
+        uint32_t npc;
+        uint32_t y;
+        uint32_t u_regs[16]; /* globals and ins */
+};
+
+struct leon_reg_win {
+        uint32_t locals[8];
+        uint32_t ins[8];
+};
+
+/* a stack frame */
+struct sparc_stackf {
+        uint32_t             locals[8];
+        uint32_t             ins[6];
+        struct sparc_stackf *fp;	 /* %i6 == %fp */
+        uint32_t             callers_pc; /* %i7 == return %pc */
+        uint8_t             *structptr;
+        uint32_t             xargs[6];
+        uint32_t             xxargs[1];
+	/* everyting allocated on the stack follows here */
+};
+
+
+#define STACKFRAME_SZ	sizeof(struct sparc_stackf)
+
+#define STACK_ALIGN 8
+
+
+int stack_migrate(void *sp, void *stack_top_new);
+
+
+#endif /* _SPARC_STACK_H_ */
diff --git a/arch/sparc/include/stacktrace.h b/arch/sparc/include/stacktrace.h
new file mode 100644
index 0000000000000000000000000000000000000000..26094690ee0539e6bec637e6236b6a52fcf15dc0
--- /dev/null
+++ b/arch/sparc/include/stacktrace.h
@@ -0,0 +1,33 @@
+/**
+ * @file   arch/sparc/include/stacktrace.h
+ */
+
+#ifndef _SPARC_STACKTRACE_H_
+#define _SPARC_STACKTRACE_H_
+
+#include <stdint.h>
+#include <stack.h>
+
+struct stack_trace {
+        uint32_t nr_entries;
+	uint32_t max_entries;
+        struct sparc_stackf **frames;
+        struct pt_regs      **regs;
+};
+
+
+#if defined(USE_STACK_TRACE_TRAP)
+/**
+ * @brief a trap handler to execute a stack trace (implemented in asm)
+ */
+void trace_trap(void);
+
+void die(void);
+#endif
+
+void save_stack_trace(struct stack_trace *trace, uint32_t sp, uint32_t pc);
+
+/* part of libgloss */
+void __flush_windows(void);
+
+#endif /* _SPARC_STACKTRACE_H_ */
diff --git a/arch/sparc/include/traps.h b/arch/sparc/include/traps.h
new file mode 100644
index 0000000000000000000000000000000000000000..1b80d592b189bd8c645582dbe51f768242d1f196
--- /dev/null
+++ b/arch/sparc/include/traps.h
@@ -0,0 +1,32 @@
+/**
+ * @file   arch/sparc/include/traps.h
+ * @ingroup traps
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ * @date   February, 2016
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+
+#ifndef _ARCH_SPARC_TRAPS_H_
+#define _ARCH_SPARC_TRAPS_H_
+
+#include <stdint.h>
+
+void trap_handler_install(uint32_t trap, void (*handler)(void));
+
+void data_access_exception_trap(void);
+void data_access_exception_trap_ignore(void);
+
+void floating_point_exception_trap(void);
+
+#endif /* _ARCH_SPARC_TRAPS_H_ */
diff --git a/arch/sparc/include/uapi/asi.h b/arch/sparc/include/uapi/asi.h
new file mode 100644
index 0000000000000000000000000000000000000000..298fe9fa52d2d95d168467e55f4e30b9aa5965f8
--- /dev/null
+++ b/arch/sparc/include/uapi/asi.h
@@ -0,0 +1,31 @@
+/**
+ * @brief Address Space Identifiers (ASI) for the LEON
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ */
+
+
+#ifndef _SPARC_ASI_H_
+#define _SPARC_ASI_H_
+
+#define ASI_LEON_NOCACHE        0x01
+#define ASI_LEON_DCACHE_MISS    ASI_LEON_NOCACHE
+
+#define ASI_LEON_CACHEREGS      0x02
+#define ASI_LEON_IFLUSH         0x10
+#define ASI_LEON_DFLUSH         0x11
+
+#define ASI_LEON2_IFLUSH	0x05
+#define ASI_LEON2_DFLUSH	0x06
+
+#define ASI_LEON_MMUFLUSH       0x18
+#define ASI_LEON_MMUREGS        0x19
+#define ASI_LEON_BYPASS         0x1c
+#define ASI_LEON_FLUSH_PAGE     0x10
+
+
+
+#define ASI_LEON_CACHEREGS_SNOOPING_BIT	(1<<27)		/* GR712RC-UM p. 44 */
+
+
+
+#endif /* _SPARC_ASI_H_ */
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 48692145a056402dc1b4dc5c68fafe0d049dbba1..e00aa128102045c013a85863637c46a876d649a5 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -9,5 +9,10 @@ obj-y += bootmem.o
 obj-y += mm.o
 obj-y += elf.o
 obj-y += module.o
+obj-y += traps.o
+obj-y += stacktrace.o
+obj-y += stack.o
+obj-y += traps/data_access_exception_trap.o
+obj-y += traps/data_access_exception.o
 
 #libs-y                 += lib/
diff --git a/arch/sparc/kernel/bootmem.c b/arch/sparc/kernel/bootmem.c
index 688c30bc416785e2fb3615cc2e6540e69de01ceb..b5a9eb07bfde16915ab3b97fa5768fa2c8a124c6 100644
--- a/arch/sparc/kernel/bootmem.c
+++ b/arch/sparc/kernel/bootmem.c
@@ -1,29 +1,139 @@
 /**
  * @file arch/sparc/kernel/bootmem.c
+ *
+ * This sets up a buddy system memory manager that handles the physical RAM
+ * banks. The kernel image RAM section itself is reserved, and a more
+ * fine-grained boot memory allocator is set up that can be used to reserve
+ * memory for low-level kernel objects such as MMU tables.
+ *
+ * Note that because we don't use a two-stage bootstrap process at this time,
+ * we will 1:1 map the relevant physical memory bank addresses through the MMU,
+ * so the low level objects may be used without a translation step for now,
+ * but we will migrate to a new kernel stack base at (2^32 - 1) once we
+ * have set up the MMU.
+ *
+ * Because of the above, user space memory will be mapped below the
+ * boot address of the kernel image, i.e. 0x40000000 for typical LEON hardware,
+ * since we want to keep our kernel context in the MMU at all times.
  */
 
 #include <page.h>
+#include <stack.h>
 
 #include <string.h>
 
 #include <kernel/printk.h>
 #include <kernel/kernel.h>
 
+#include <chunk.h>
+
+
+/* the pool we use for our boot memory allocator */
+static struct chunk_pool phys_mem_pool;
+
+
+/**
+ * @brief boot memory allocator wrapper
+ * @param size the number of bytes to allocate
+ *
+ * @return a pointer to a buffer or NULL on error
+ */
+
+static void *bootmem_alloc_internal(size_t size)
+{
+#if (CONFIG_SPARC_BOOTMEM_REQUEST_NEW_ON_DEMAND)
+	static int blocked;
+
+	if (blocked) {
+		pr_crit("BOOTMEM: out of memory\n");
+		return NULL;
+	}
+	blocked = 1;
+#endif
+
+#if (BOOTMEM_CHUNKSIZE > PAGE_SIZE)
+	return page_map_reserve_chunk(BOOTMEM_CHUNKSIZE);
+#else
+	return page_alloc();
+#endif
+}
+
+
+/**
+ * @brief boot memory free wrapper
+ * @param addr the address to free
+ */
+
+static void bootmem_free_internal(void *ptr)
+{
+	page_free(ptr);
+}
+
+/**
+ * @brief finds the actual size of an allocated buffer
+ * @param addr the address to look up
+ *
+ * @return the buffer size, or 0 if invalid address or not found
+ */
+
+static size_t bootmem_get_alloc_size(void *ptr)
+{
+	return page_map_get_chunk_size(ptr);
+}
+
+
+/**
+ * @brief allocate a buffer from the boot memory allocator
+ *
+ * @param size the number of bytes to allocate
+ *
+ * @return a pointer to a buffer or NULL on error
+ */
+
+void *bootmem_alloc(size_t size)
+{
+	void *ptr;
+
+	ptr = chunk_alloc(&phys_mem_pool, size);
+
+	if (!ptr) {
+		pr_emerg("BOOTMEM: allocator out of memory\n");
+		BUG();
+	}
+
+	return ptr;
+}
+
+
+/**
+ * @brief release a buffer to the boot memory allocator
+ *
+ * @param addr the address to release
+ */
+
+void bootmem_free(void *ptr)
+{
+	chunk_free(&phys_mem_pool, ptr);
+}
+
+
+/**
+ * @brief initialise the boot memory
+ */
 
-/* TODO still demo code */
 void bootmem_init(void)
 {
 	int i;
 
+	int ret;
+
 	unsigned long base_pfn;
 	unsigned long start_pfn;
 	unsigned long start_img_pfn;
 	unsigned long end_pfn = 0UL;
 	unsigned long mem_size;
 
-	void *pages[2048];
-
-	int t=0;
+	unsigned long node = 0;
 
 	struct page_map_node **pg_node;
 
@@ -36,12 +146,12 @@ void bootmem_init(void)
 	 * start symbol in image, which hopefully coincides with the start
 	 * of the RAM we are running from.
 	 */
-	start_img_pfn  = (unsigned long) PAGE_ALIGN((unsigned long) &start);
+	start_img_pfn = PAGE_ALIGN((unsigned long) &start);
 
 	/* start allocatable memory with page aligned address of last symbol in
 	 * image, everything before will be reserved
 	 */
-	start_pfn  = (unsigned long) PAGE_ALIGN((unsigned long) &end);
+	start_pfn = PAGE_ALIGN((unsigned long) &end);
 
 	/* locate the memory bank we're in
 	 */
@@ -50,6 +160,9 @@ void bootmem_init(void)
 
 	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
 
+		/* do you feel luck, punk? */
+		BUG_ON(i > SPARC_PHYS_BANKS);
+
 		if (start_pfn < sp_banks[i].base_addr)
 			continue;
 
@@ -85,8 +198,8 @@ void bootmem_init(void)
 	end_pfn = (unsigned long) __pa(end_pfn);
 	//end_pfn = PHYS_PFN(end_pfn);
 
-	pr_info("BOOTMEM: start page frame number: 0x%lx\n"
-		"BOOTMEM:   end page frame number: 0x%lx\n",
+	pr_info("BOOTMEM: start page frame at 0x%lx\n"
+		"BOOTMEM:   end page frame at 0x%lx\n",
 	       start_pfn, end_pfn);
 
 
@@ -95,7 +208,8 @@ void bootmem_init(void)
 	 * on top of that
 	 */
 
-	pg_node = MEM_PAGE_NODE(0);
+	pg_node = MEM_PAGE_NODE(node);
+	node++;
 
 	(*pg_node) = &mm_init_page_node;
 
@@ -119,92 +233,59 @@ void bootmem_init(void)
 	BUG_ON(!page_map_reserve_chunk(start_pfn - base_pfn));
 
 
-	mm_dump_stats((*pg_node)->pool);
-
-	page_alloc();
-	page_alloc();
-	page_alloc();
-	page_alloc();
-	page_alloc();
-
-	mm_dump_stats((*pg_node)->pool);
+	/* our image has been reserved, now set up the boot memory allocator */
+	chunk_pool_init(&phys_mem_pool, STACK_ALIGN, &bootmem_alloc_internal,
+			&bootmem_free_internal, &bootmem_get_alloc_size);
 
 
+	/* now add the remaining memory banks */
+	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
 
-	pg_node = MEM_PAGE_NODE(1);
-
-	BUG_ON(!pg_node);
-
-	(*pg_node)		      = page_map_reserve_chunk(
-					sizeof(struct page_map_node));
-
-	(*pg_node)->pool	      = page_map_reserve_chunk(
-					sizeof(struct mm_pool));
-
-	bzero((*pg_node)->pool, sizeof(struct mm_pool));
-
-	(*pg_node)->pool->block_order = page_map_reserve_chunk(
-					sizeof(struct list_head) *
-					MM_BLOCK_ORDER_MAX);
-	(*pg_node)->pool->alloc_order = page_map_reserve_chunk(
-					MM_INIT_NUM_BLOCKS);
-	(*pg_node)->pool->blk_free    = page_map_reserve_chunk(
-					MM_INIT_LEN_BITMAP *
-					sizeof(unsigned long));
-
-
-	base_pfn = (unsigned long) page_map_reserve_chunk(1024*4*4);
-	page_map_add(base_pfn, base_pfn + 1024*4*4, PAGE_SIZE);
-
-
-
-	pg_node = MEM_PAGE_NODE(2);
-	BUG_ON(!pg_node);
-
-	(*pg_node)		      = page_map_reserve_chunk(
-					sizeof(struct page_map_node));
-	BUG_ON(!(*pg_node));
+		BUG_ON(i > SPARC_PHYS_BANKS);
 
-	(*pg_node)->pool	      = page_map_reserve_chunk(
-					sizeof(struct mm_pool));
+		/* this one has already been added */
+		if (base_pfn == sp_banks[i].base_addr)
+			continue;
 
-	bzero((*pg_node)->pool, sizeof(struct mm_pool));
-	(*pg_node)->pool->block_order = page_map_reserve_chunk(
-					sizeof(struct list_head) *
-					MM_BLOCK_ORDER_MAX);
-	(*pg_node)->pool->alloc_order = page_map_reserve_chunk(
-					MM_INIT_NUM_BLOCKS);
-	(*pg_node)->pool->blk_free    = page_map_reserve_chunk(
-					MM_INIT_LEN_BITMAP *
-					sizeof(unsigned long));
+	
+		pg_node = MEM_PAGE_NODE(node);
+		node++;
 
+		if(!pg_node) {
+			pr_err("BOOTMEM: initial page map holds less nodes "
+			       "than number of configured memory banks.\n");
+			break;
+		}
 
-	base_pfn = (unsigned long) page_map_reserve_chunk(1024*4*4);
-	page_map_add(base_pfn, base_pfn + 1024*4*4, PAGE_SIZE);
+		/* let's assume we always have enough memory, because if we
+		 * don't, there is a serious configuration problem anyways
+		 */
 
+		(*pg_node) = (struct page_map_node *)
+				bootmem_alloc(sizeof(struct page_map_node));
 
-	while (t < 1740)
-		pages[t++] = page_alloc();
+		(*pg_node)->pool = (struct mm_pool *)
+					bootmem_alloc(sizeof(struct mm_pool));
 
-	pages[t++] = page_alloc();
-	pages[t++] = page_alloc();
-	pages[t++] = page_alloc();
-	pages[t++] = page_alloc();
-	pages[t++] = page_alloc();
-	pages[t++] = page_alloc();
-	pages[t++] = page_alloc();
-	pages[t++] = page_alloc();
-	pages[t++] = page_alloc();
+		bzero((*pg_node)->pool, sizeof(struct mm_pool));
 
-	/* NULL */
-	pages[t++] = page_alloc();
+		(*pg_node)->pool->block_order = (struct list_head *)
+					bootmem_alloc(sizeof(struct list_head)
+						* MM_BLOCK_ORDER_MAX);
+		(*pg_node)->pool->alloc_order = (unsigned char *)
+					bootmem_alloc(MM_INIT_NUM_BLOCKS);
 
-	page_free(pages[--t]);
-	page_free(pages[--t]);
-	page_free(pages[--t]);
-	page_free(pages[--t]);
-	page_free(pages[--t]);
-	page_free(pages[--t]);
+		(*pg_node)->pool->blk_free = (unsigned long *)
+					bootmem_alloc(MM_INIT_LEN_BITMAP
+						* sizeof(unsigned long));
 
+		ret = page_map_add(sp_banks[i].base_addr,
+				sp_banks[i].base_addr + sp_banks[i].num_bytes,
+				PAGE_SIZE);
 
+		if (ret) {
+			pr_emerg("BOOTMEM: cannot add page map node\n");
+			BUG();
+		}
+	}
 }
diff --git a/arch/sparc/kernel/init.c b/arch/sparc/kernel/init.c
index faf296604e7e0175c678b14368947c7df85a5699..c5d6727260397b4bb50e873e6029d53867b29d62 100644
--- a/arch/sparc/kernel/init.c
+++ b/arch/sparc/kernel/init.c
@@ -3,8 +3,10 @@
  */
 
 #include <mm.h>
+#include <srmmu.h>
 
 void paging_init(void)
 {
 	bootmem_init();
+	mm_mmu_paging_init();
 }
diff --git a/arch/sparc/kernel/mm.c b/arch/sparc/kernel/mm.c
index 42d5a9f96a3e8d3fdb52cab0d4b490c3c4e15a8b..4adfabd5827424652bfb395b01f86c234a2b6547 100644
--- a/arch/sparc/kernel/mm.c
+++ b/arch/sparc/kernel/mm.c
@@ -4,7 +4,15 @@
 
 #include <mm.h>
 
+#include <kernel/kernel.h>
+#include <kernel/printk.h>
 
+#include <page.h>
+#include <srmmu.h>
+#include <srmmu_access.h>
+#include <traps.h>
+#include <cpu_type.h>
+#include <errno.h>
 
 /* things we need statically allocated in the image (i.e. in .bss)
  * at boot
@@ -14,3 +22,342 @@ unsigned long phys_base;
 unsigned long pfn_base;
 
 struct sparc_physical_banks sp_banks[SPARC_PHYS_BANKS + 1];
+
+
+
+
+
+
+/**
+ * for now, this is our primitive per-process (we really have only one...)
+ * system break tracker
+ *
+ * sbrk: the current program break
+ * addr_low the legal lower boundary
+ * addr_hi the legal upper boundary
+ *
+ * note: we don't have user/kernel memory segmentation yet
+ */
+
+static struct mm_sbrk {
+	unsigned long sbrk;
+
+	unsigned long addr_lo;
+	unsigned long addr_hi;
+} mm_proc_mem [SRMMU_CONTEXTS];
+
+static unsigned long _mmu_ctx;
+
+
+
+/* XXX: dummy, move out of here */
+enum sparc_cpus sparc_cpu_model;
+
+
+
+/**
+ * @brief if the SRMMU is not supported
+ */
+
+static void mm_srmmu_is_bad(void)
+{
+        pr_emerg("MM: No supported SRMMU type found.\n");
+	BUG();
+}
+
+
+/**
+ * @brief configure MMU on a LEON
+ *
+ * TODO stuff...
+ */
+
+static void mm_init_leon(void)
+{
+	trap_handler_install(0x9, data_access_exception_trap);
+	srmmu_init(&bootmem_alloc, &bootmem_free);
+
+	/* 1:1 map full range of highmem */
+	srmmu_do_large_mapping_range(0, HIGHMEM_START, HIGHMEM_START,
+				(0xFFFFFFFF - HIGHMEM_START) /
+				SRMMU_LARGE_PAGE_SIZE + 1,
+				(SRMMU_CACHEABLE | SRMMU_ACC_S_RWX_2));
+
+	mm_set_mmu_ctx(0);
+
+	srmmu_enable_mmu();
+}
+
+
+/**
+ * @brief probe for supported mmu type
+ */
+
+static void get_srmmu_type(void)
+{
+	/* we only support the GRLIB SRMMU, its implementation and version
+	 * fields are set to 0, so we only check for a matching CPU model
+	 * else we assume a bad SRMMU
+	 */
+
+	if (srmmu_get_mmu_ver())	/* GR712RC == 0 */
+		if (srmmu_get_mmu_ver() != 1)	/* apparently MPPB */
+			goto bad;
+
+	if (srmmu_get_mmu_impl())	/* GR712RC == 0 */
+		if (srmmu_get_mmu_impl() != 8)	/* apparently MPPB */
+		goto bad;
+
+	if (sparc_cpu_model == sparc_leon) {
+		mm_init_leon();
+		return;
+	}
+
+bad:
+	mm_srmmu_is_bad();
+}
+
+
+/**
+ * @brief load MMU support
+ * @note this is called from setup_arch() for LEON
+ */
+
+static void mm_load_mmu(void)
+{
+	get_srmmu_type();
+
+	/* XXX: we also might want to:
+	 *	- load TLB operations if needed (for SMP)
+	 *	- IOMMU setup if needed
+	 *	- initialise SMP
+	 */
+}
+
+
+/**
+ * @brief set the active MMU context
+ *
+ * @param ctx the MMU context to set
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int mm_set_mmu_ctx(unsigned long ctx)
+{
+
+	if (srmmu_select_ctx(ctx))
+		return -EINVAL;
+
+	/* if necessary, initialise the program break */
+	if (!mm_proc_mem[ctx].sbrk) {
+		mm_proc_mem[ctx].sbrk    = VMALLOC_START;
+		mm_proc_mem[ctx].addr_lo = VMALLOC_START;
+		mm_proc_mem[ctx].addr_hi = VMALLOC_END;
+	}
+
+	_mmu_ctx = ctx;
+
+	return 0;
+}
+
+
+/**
+ * @brief get the active MMU context
+ *
+ * @returns the active MMU context number
+ */
+
+unsigned long mm_get_mmu_ctx(void)
+{
+	return _mmu_ctx;
+}
+
+void mm_release_mmu_mapping(unsigned long va_start, unsigned long va_stop)
+{
+	unsigned long ctx;
+
+
+	ctx = mm_get_mmu_ctx();
+
+	srmmu_release_pages(ctx, va_start, va_stop, page_free);
+}
+
+void *kernel_sbrk(intptr_t increment)
+{
+	long brk;
+	long oldbrk;
+
+	unsigned long ctx;
+
+
+	ctx = mm_get_mmu_ctx();
+
+	if (!increment)
+		 return (void *) mm_proc_mem[ctx].sbrk;
+
+	oldbrk = mm_proc_mem[ctx].sbrk;
+
+	brk = oldbrk + increment;
+
+	if (brk < mm_proc_mem[ctx].addr_lo)
+		return (void *) -1;
+
+	if (brk >  mm_proc_mem[ctx].addr_hi)
+		return (void *) -1;
+
+	/* try to release pages if we decremented below a page boundary */
+	if (increment < 0) {
+		if (PAGE_ALIGN(brk) < PAGE_ALIGN(oldbrk - PAGE_SIZE)) {
+			printk("SBRK: release %lx (%lx)\n", brk, PAGE_ALIGN(brk));
+			mm_release_mmu_mapping(PAGE_ALIGN(brk), oldbrk);
+		}
+	}
+
+	mm_proc_mem[ctx].sbrk = brk;
+
+	pr_debug("SBRK: moved %08lx -> %08lx\n", oldbrk, brk);
+
+	return (void *) oldbrk;
+}
+
+
+
+
+void mm_mmu_trap(void)
+{
+	unsigned long ctx;
+
+	struct srmmu_fault_status status;
+	unsigned int addr;
+	unsigned int alloc;
+	static int last;
+
+	status = srmmu_get_mmu_fault_status();
+
+	pr_debug("MM: MMU Status:\n"
+	       "MM: ===========\n");
+
+	pr_debug("MM:\tAccess type: ");
+	switch(status.access_type)  {
+	case SRMMU_FLT_AT_R__U_DATA:
+		pr_debug("Read from User data\n"); break;
+	case SRMMU_FLT_AT_R__S_DATA:
+		pr_debug("Read from SuperUser data\n"); break;
+	case SRMMU_FLT_AT_RX_U_INST:
+		pr_debug("Read/Exec from User instruction\n"); break;
+	case SRMMU_FLT_AT_RX_S_INST:
+		pr_debug("Read/Exec from SuperUser instruction\n"); break;
+	case SRMMU_FLT_AT_W__U_DATA:
+		pr_debug("Write to User data\n"); break;
+	case SRMMU_FLT_AT_W__S_DATA:
+		pr_debug("Write to SuperUser instruction\n"); break;
+	case SRMMU_FLT_AT_W__U_INST:
+		pr_debug("Write to User instruction\n"); break;
+	case SRMMU_FLT_AT_W__S_INST:
+		pr_debug("Write to SuperUser instruction\n"); break;
+	default:
+		pr_debug("Unknown\n"); break;
+	}
+
+	pr_debug("MM:\tFault type: ");
+	switch(status.fault_type)  {
+	case SRMMU_FLT_FT_NONE:
+		pr_debug("None\n"); break;
+	case SRMMU_FLT_FT_INVALID_ADDR:
+		if (status.fault_address_vaild) {
+
+			addr = srmmu_get_mmu_fault_address();
+
+			if (!addr) {
+				pr_crit("NULL pointer violation "
+					"in call from %p\n",
+					__builtin_return_address(1));
+				BUG();
+			}
+
+
+			pr_debug("Invalid Address/Not mapped: 0x%08x\n",
+			       srmmu_get_mmu_fault_address() >> 24);
+
+
+			ctx = mm_get_mmu_ctx();
+
+			if (addr < mm_proc_mem[ctx].addr_lo) {
+
+				pr_crit("Access violation: RESERVED (0x%08lx) "
+					"in call from %p\n",
+					addr,
+					__caller(1));
+				BUG();
+			}
+
+			if (addr > HIGHMEM_START) {
+				pr_debug("Access violation: HIGHMEM\n");
+				BUG();
+			}
+
+
+			if (addr < mm_proc_mem[ctx].sbrk) {
+				alloc = (unsigned long) page_alloc();
+				if (!alloc) {
+					pr_crit("MM:\t Out of physical memory %lx\n", last);
+					BUG();
+				}
+
+				last = alloc;
+
+				pr_debug("MM: Allocating page %lx -> %lx\n",addr, (unsigned
+									int) alloc);
+
+				srmmu_do_small_mapping(ctx, addr,
+					alloc,
+					(SRMMU_CACHEABLE | SRMMU_ACC_S_RW_2));
+			} else {
+				pr_crit("Access violation: system break "
+					"in call from %p\n",
+					__builtin_return_address(1));
+				BUG();
+			}
+		}
+		break;
+	case SRMMU_FLT_FT_PROTECTION:
+		pr_debug("Protection Error\n"); break;
+	case SRMMU_FLT_FT_PRIV_VIOLATION:
+		pr_debug("Priviledge Violation\n"); break;
+	case SRMMU_FLT_FT_TRANSLATION:
+		pr_debug("Translation Error\n"); break;
+	case SRMMU_FLT_FT_ACCESS_BUS:
+		pr_debug("Bus Access Error\n"); break;
+	case SRMMU_FLT_FT_INTERNAL:
+		pr_debug("Internal Error\n"); break;
+	default:
+		pr_debug("Unknown\n"); break;
+	}
+
+	pr_debug("MM:\tPage table level: %d\n", status.page_table_level);
+
+	if (status.fault_address_vaild)
+		pr_debug("MM:\tVirtual address 0x%x", srmmu_get_mmu_fault_address());
+
+	if (status.overwrite)
+		pr_debug("\tMultiple errors since last read of status recorded");
+
+
+	pr_debug("\nMM:\n");
+
+}
+
+
+
+/**
+ *
+ * @brief initialise paging on the SRMMU
+ */
+
+void mm_mmu_paging_init(void)
+{
+	/* XXX: must be configured earlier by reading appropriate register */
+	sparc_cpu_model = sparc_leon;
+	mm_load_mmu();
+}
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
index 3787f6039865e7d582e58c9faecd170f107fd053..f25f695fb311eea52d2df883cf4583718e79d899 100644
--- a/arch/sparc/kernel/setup.c
+++ b/arch/sparc/kernel/setup.c
@@ -8,6 +8,47 @@
 #include <mm.h>
 #include <compiler.h>
 
+#include <page.h>
+#include <stack.h>
+#include <kernel/kmem.h>
+
+void *_kernel_stack_top;
+void *_kernel_stack_bottom;
+
+
+/**
+ * @brief reserve a stack area for the kernel
+ *
+ * @warn Since we allocate the kernel stack using kmalloc instead of placing it
+ *	 in a custom area, there is a real change of violating the bottom
+ *	 boundary, so make sure you allocate enough pages to fit your needs.
+ *	 Note that since kmalloc() works via kernel_sbrk() and moving the system
+ *	 break does not actually reserve pages until they are accessed, you
+ *	 have to initialise the stack area, i.e. actually reserve the pages,
+ *	 unless you have a custom interrupt/trap stack. Otherwise the kernel
+ *	 cannot perform stack access to an unmapped page, because that would
+ *	 require a mapped page...
+ *
+ * XXX this needs to be addressed at some point, but probably only after we
+ *     have a kernel bootstrap implemented and we may define custom reserved
+ *     areas more freely.
+ */
+
+#warning "Using fixed-size kernel stack"
+static void reserve_kernel_stack(void)
+{
+	const size_t k_stack_sz = KERNEL_STACK_PAGES * PAGE_SIZE;
+
+
+	/* the bottom of the stack */
+	_kernel_stack_bottom = kcalloc(k_stack_sz + STACK_ALIGN, sizeof(char));
+	BUG_ON(!_kernel_stack_bottom);
+
+	/* the (aligned) top of the stack */
+	_kernel_stack_top = (void *) (char *) _kernel_stack_bottom + k_stack_sz;
+	_kernel_stack_top = ALIGN_PTR(_kernel_stack_top, STACK_ALIGN);
+}
+
 
 /**
  * @brief configure available memory banks
@@ -19,14 +60,16 @@
 
 static void mem_init(void)
 {
-	memset(&sp_banks, 0x0, ARRAY_SIZE(sp_banks));
-
 	sp_banks[0].base_addr = 0x40000000;
 	sp_banks[0].num_bytes = 0x00800000;
-#if 0
+
+#if (SPARC_PHYS_BANKS > 0)
 	sp_banks[1].base_addr = 0x60000000;
 	sp_banks[1].num_bytes = 0x04000000;
+#else
+#warning "Configuration error: SPARC_PHYS_BANKS size insufficient."
 #endif
+
 }
 
 
@@ -37,5 +80,12 @@ static void mem_init(void)
 void setup_arch(void)
 {
 	mem_init();
+
 	paging_init();
+
+	BUG_ON(!kmem_init());
+
+	reserve_kernel_stack();
+
+	BUG_ON(stack_migrate(NULL, _kernel_stack_top));
 }
diff --git a/arch/sparc/kernel/stack.c b/arch/sparc/kernel/stack.c
new file mode 100644
index 0000000000000000000000000000000000000000..e4da1b52dace07e2c0a5f7c66edfb4720596180e
--- /dev/null
+++ b/arch/sparc/kernel/stack.c
@@ -0,0 +1,93 @@
+/**
+ * @file arch/sparc/kernel/stack.c
+ */
+
+
+#include <stacktrace.h>
+#include <asm/leon.h>
+
+#include <string.h> /* memcpy() */
+#include <stdio.h>
+#include <errno.h>
+
+
+/**
+ * @brief migrate a stack
+ *
+ * @param sp the (bottom) stack pointer of the old stack
+ * @param stack_top the top of the new stack area
+ *
+ * @note the new stack area is assumed to at least hold the old stack
+ * @note remember that SPARC stacks grow from top to bottom
+ */
+
+int stack_migrate(void *sp, void *stack_top_new)
+{
+	int i;
+
+	void *sp_new;
+
+	unsigned long stack_sz;
+	unsigned long stack_top;
+	unsigned long stack_bot;
+
+	unsigned long stackf_sz;
+
+
+	struct stack_trace x;
+	struct sparc_stackf *frames[30];
+	struct pt_regs      *regs[30];
+
+	struct sparc_stackf *stack;
+
+
+
+	if (!stack_top_new)
+		return -EINVAL;
+
+	/* migrate the current stack */
+	if (!sp)
+		sp = (void *) leon_get_sp();
+
+	/* 30 should be more than enough */
+	x.max_entries = 30;
+	x.nr_entries  = 0;
+	x.frames      = frames;
+	x.regs        = regs;
+
+
+	/* this also flushes SPARC register windows */
+	save_stack_trace(&x, (uint32_t) sp, 0);
+
+
+	stack_top = (unsigned long) frames[x.nr_entries - 1];
+	stack_bot = (unsigned long) frames[0];
+
+	stack_sz = stack_top - stack_bot;
+
+	/* new bottom of stack */
+	sp_new = (void *) ((char *) stack_top_new - stack_sz);
+
+	stack = (struct sparc_stackf *) sp_new;
+
+	/* copy the old stack */
+	memcpy(sp_new, (void *) stack_bot, stack_sz);
+
+	/* adjust frame addresses for all but top frame */
+	for(i = 0; i < (x.nr_entries - 1); i++) {
+
+		stackf_sz = (unsigned long) frames[i]->fp
+			  - (unsigned long) frames[i];
+		stack->fp = (void *) ((char *) stack + stackf_sz);
+
+		stack = stack->fp;
+	}
+
+	/* go back to new bottom of stack */
+	stack = (struct sparc_stackf *) sp_new;
+
+	/* update frame pointer so we jump to the migrated stack on return */
+	leon_set_fp((unsigned long) stack->fp);
+
+	return 0;
+}
diff --git a/arch/sparc/kernel/stacktrace.c b/arch/sparc/kernel/stacktrace.c
new file mode 100644
index 0000000000000000000000000000000000000000..889ffaf600934f3685295aae03ba720936931eae
--- /dev/null
+++ b/arch/sparc/kernel/stacktrace.c
@@ -0,0 +1,106 @@
+/**
+ * @file   arch/sparc/kernel/stacktrace.c
+ */
+
+#include <stdlib.h>
+
+#include <compiler.h>
+#include <asm/leon.h>
+#include <stacktrace.h>
+#include <kernel/printk.h>
+
+
+
+/**
+ * @brief validates the stack pointer address
+ */
+static int stack_valid(uint32_t sp)
+{
+	if (sp & (STACK_ALIGN - 1) || !sp)
+		return 0;
+
+	return 1;
+}
+
+
+/**
+ * @brief performs a stack trace
+ *
+ * @param trace  a struct stack_trace
+ * @param sp     a stack/frame pointer
+ * @param pc     a program counter
+ *
+ * @note When being called from a trap, the pc in %o7 is NOT the return program
+ *       counter of the trapped function, so a stack/frame pointer by itself
+ *       is not enough to provide a proper trace, hence the pc argument
+ */
+
+void save_stack_trace(struct stack_trace *trace, uint32_t sp, uint32_t pc)
+{
+	struct sparc_stackf *sf;
+	struct pt_regs *regs;
+
+
+	if (!stack_valid(sp))
+		return;
+
+	/* flush register windows to memory*/
+	leon_reg_win_flush();
+
+	do {
+		if (!stack_valid(sp))
+			break;
+
+
+		sf   = (struct sparc_stackf *) sp;
+		regs = (struct pt_regs *) (sf + 1);
+
+		trace->frames[trace->nr_entries] = sf;
+		trace->regs[trace->nr_entries]   = regs;
+
+		trace->nr_entries++;
+
+		pc = sf->callers_pc;
+		sp = (uint32_t) sf->fp;
+
+	} while (trace->nr_entries < trace->max_entries);
+}
+
+
+
+#if defined(USE_STACK_TRACE_TRAP)
+
+/**
+ * @brief a generic shutdown function
+ * TODO: replace with whatever we need in the end
+ */
+
+void die(void)
+{
+	BUG();
+}
+
+
+/**
+ * @brief executes a stack trace
+ *
+ * @param fp a frame pointer
+ * @param pc a program counter
+ *
+ * @note this is called by the trap handler
+ */
+
+void trace(uint32_t fp, uint32_t pc)
+{
+	uint32_t entries[30];
+
+	struct stack_trace x;
+
+
+	x.max_entries = 30;
+	x.nr_entries  = 0;
+	x.entries     = entries;
+
+	save_stack_trace(&x, fp, pc);
+}
+#endif
diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c
new file mode 100644
index 0000000000000000000000000000000000000000..534f3594806720bda4fe6d25543a50410362bd8b
--- /dev/null
+++ b/arch/sparc/kernel/traps.c
@@ -0,0 +1,119 @@
+/**
+ * @file   arch/sparc/kernel/traps.c
+ * @ingroup traps
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ * @author Linus Torvalds et al.
+ * @date   September, 2015
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @defgroup traps Trap Table access
+ * @brief Implements functionality to access or modify SPARC v8 MVT trap
+ *	  table entries
+ *
+ *
+ *
+ * ## Overview
+ *
+ * This module implements functionality to access or modify SPARC v8 MVT trap
+ * table entries.
+ *
+ * ## Mode of Operation
+ *
+ * None
+ *
+ * ## Error Handling
+ *
+ * None
+ *
+ * ## Notes
+ *
+ * - functionality will be added as needed
+ *
+ *
+ */
+
+#include <traps.h>
+#include <asm/io.h>
+#include <compiler.h>
+
+
+/**
+ * @brief installs a trap handler that executes a long jump
+ *
+ * @param trap a trap entry number
+ * @param handler a function to call when the trap is triggerd
+ *
+ * @note I'm not sure why this must be nested in another function call. If it
+ *       isn't, the trap entry seemingly does not update properly. It might have
+ *       to do with the rotation of the register window or some caching
+ *       mechanism I'm not aware of at this time. The writes to the table are
+ *       uncached, so the data cache is likely not the issue, and flusing it
+ *       won't do anything either. When not nested in a wrapper, it however
+ *       appears to work as expected if a function that in turn calls a
+ *       function is executed after the trap entry is written - but it does not
+ *       if the same function is called after this one has returned. O.o
+ *
+ * Installs a custom handler into a specified trap table entry. Note that it is
+ * not possible to install just any function, since traps are disabled when the
+ * call is executed and any further trap (such as window over/underflow) will
+ * force the processor to jump to trap 0 (i.e. reset). See lib/asm/trace_trap.S
+ * for a working example.
+ *
+ *
+ */
+
+static void trap_longjump_install(uint32_t trap, void (*handler)(void))
+{
+	uint32_t *trap_base;
+	uint32_t tbr;
+	uint32_t h;
+
+
+	h = (unsigned int) handler;
+
+	/* extract the trap table address from %tbr (skips lower bits 0-11) */
+	__asm__ __volatile__("rd %%tbr, %0" : "=r" (tbr));
+
+	/* calculate offset to trap, each entry is 4 machine words long */
+	trap_base = (uint32_t *)((tbr & ~0xfff) + (trap << 4));
+
+	/* set up the trap entry:
+	 * 0x29000000 sethi %hi(handler), %l4
+	 * 0x81c52000 jmpl  %l4 + %lo(handler), %g0
+	 * 0x01000000 rd    %psr, %l0
+	 * 0x01000000 nop
+	 */
+	iowrite32be(((h >> 10) & 0x3fffff) | 0x29000000, trap_base + 0);
+	iowrite32be((h        &    0x3ff) | 0x81c52000, trap_base + 1);
+	iowrite32be(0xa1480000,                         trap_base + 2);
+	iowrite32be(0x01000000,                         trap_base + 3);
+
+	barrier();
+}
+
+/**
+ * @brief installs a custom trap handler
+ *
+ * @param trap a trap entry number
+ * @param handler a function to call when the trap is triggerd
+ *
+ * Installs a custom handler into a specified trap table entry. Note that it is
+ * not possible to install just any function, since traps are disabled when the
+ * call is executed and any further trap (such as window over/underflow) will
+ * force the processor to jump to trap 0 (i.e. reset). See lib/asm/trace_trap.S
+ * for a working example.
+ */
+
+void trap_handler_install(uint32_t trap, void (*handler)(void))
+{
+	trap_longjump_install(trap, handler);
+}
diff --git a/arch/sparc/kernel/traps/data_access_exception.c b/arch/sparc/kernel/traps/data_access_exception.c
new file mode 100644
index 0000000000000000000000000000000000000000..5bfa4acef2bb7eb04bcc0cf20fd4bb2b156e95d0
--- /dev/null
+++ b/arch/sparc/kernel/traps/data_access_exception.c
@@ -0,0 +1,13 @@
+/**
+ * @file arch/sparc/kernel/data_access_exception.c
+ *
+ */
+
+#include <kernel/printk.h>
+#include <mm.h>
+
+
+void data_access_exception(void)
+{
+	mm_mmu_trap();
+}
diff --git a/arch/sparc/kernel/traps/data_access_exception_trap.S b/arch/sparc/kernel/traps/data_access_exception_trap.S
new file mode 100644
index 0000000000000000000000000000000000000000..65445e42e30b4eb01fe1742943e09682a450cdcb
--- /dev/null
+++ b/arch/sparc/kernel/traps/data_access_exception_trap.S
@@ -0,0 +1,78 @@
+/**
+ * @file arch/sparc/kernel/traps/data_access_exception_trap.S
+ * @brief this is a function that is called by a custom trap handler to handle
+ *        an MMU or EDAC trap
+ *
+ * @todo this is BCC-specific
+ */
+
+
+
+#define SAVE_ALL_HEAD \
+        sethi   %hi(leonbare_trapsetup), %l4; \
+        jmpl    %l4 + %lo(leonbare_trapsetup), %l6;
+#define SAVE_ALL \
+        SAVE_ALL_HEAD \
+         nop;
+
+
+#define FW_REGS_SZ     0x90 /* 36*4 */
+#define SF_REGS_SZ     0x60 /* 24*4 */
+
+/* All traps low-level code here must end with this macro. */
+#define RESTORE_ALL b leonbare_trapreturn; clr %l6;
+
+
+
+	.text
+	.align 4
+	.globl  data_access_exception_trap
+
+
+
+#define PSR_ET        0x00000020         /* enable traps field */
+#define PSR_PIL_MASK  0x00000F00	/* processor interrupt level */
+
+data_access_exception_trap:
+
+	/* Since we do not overwrite values either in RAM or in FLASH, we cannot
+	 * continue from the original instruction or the same trap will occur,
+	 * hence we have to point %pc to %npc and increment %npc to the
+	 * following instruction
+         */
+	
+	/* ...but we can for the MMU
+	 mov	%l2, %l1
+	 add	%l2, 4, %l2
+	*/
+
+	rd	%wim, %l3	/* trap setup needs the %wim in %l3 */
+	
+	SAVE_ALL	/* create a trap window */
+
+	/* re-enable traps but not interrupts (set level to max) */
+	or %l0, PSR_PIL_MASK, %o0
+	wr %o0, PSR_ET, %psr
+	nop
+	nop
+	nop
+
+	call data_access_exception
+	 add	%sp, FW_REGS_SZ + 8 + SF_REGS_SZ , %o1
+
+
+	RESTORE_ALL	/* also returns */
+
+
+
+
+
+	.globl  data_access_exception_trap_ignore
+
+data_access_exception_trap_ignore:
+
+	/* see above */
+	mov	%l2, %l1
+	add	%l2, 4, %l2
+	jmp	%l1
+	rett	%l2
diff --git a/arch/sparc/kernel/traps/floating_point_exception_trap.S b/arch/sparc/kernel/traps/floating_point_exception_trap.S
new file mode 100644
index 0000000000000000000000000000000000000000..8405294e97f4b2dbcf459ee7f99505bd970a2bb2
--- /dev/null
+++ b/arch/sparc/kernel/traps/floating_point_exception_trap.S
@@ -0,0 +1,72 @@
+/**
+ * @file arch/sparc/kernel/traps/floating_point_exception_trap.S
+ *
+ * @brief this is a function that is called by a custom trap handler to handle
+ *        a floating point exception
+ *
+ * @todo this is BCC-specific
+ */
+
+
+
+#define SAVE_ALL_HEAD \
+        sethi   %hi(leonbare_trapsetup), %l4; \
+        jmpl    %l4 + %lo(leonbare_trapsetup), %l6;
+#define SAVE_ALL \
+        SAVE_ALL_HEAD \
+         nop;
+
+
+#define FW_REGS_SZ     0x90 /* 36*4 */
+#define SF_REGS_SZ     0x60 /* 24*4 */
+
+/* All traps low-level code here must end with this macro. */
+#define RESTORE_ALL b leonbare_trapreturn; clr %l6;
+
+
+
+	.text
+	.align 4
+	.globl  floating_point_exception_trap
+
+
+
+#define PSR_ET        0x00000020         /* enable traps field */
+#define PSR_PIL_MASK  0x00000F00	/* processor interrupt level */
+
+floating_point_exception_trap:
+
+
+	mov	%l2, %l1
+	add	%l2, 4, %l2
+
+	rd	%wim, %l3	/* trap setup needs the %wim in %l3 */
+	
+	SAVE_ALL	/* create a trap window */
+
+	/* re-enable traps but not interrupts (set level to max) */
+	or %l0, PSR_PIL_MASK, %o0
+	wr %o0, PSR_ET, %psr
+	nop
+	nop
+	nop
+
+	call fpe_trap
+	 add	%sp, FW_REGS_SZ + 8 + SF_REGS_SZ , %o1
+
+
+	RESTORE_ALL	/* also returns */
+
+
+
+
+
+	.globl  floating_point_exception_trap_ignore
+
+floating_point_exception_trap_ignore:
+
+	/* see above */
+	mov	%l2, %l1
+	add	%l2, 4, %l2
+	jmp	%l1
+	rett	%l2
diff --git a/arch/sparc/kernel/traps/trace_trap.S b/arch/sparc/kernel/traps/trace_trap.S
new file mode 100644
index 0000000000000000000000000000000000000000..2d60f4c00a147b413291fa7510856d5cc293ebfd
--- /dev/null
+++ b/arch/sparc/kernel/traps/trace_trap.S
@@ -0,0 +1,34 @@
+/**
+ * @file arch/sparc/kernel/traps/trace_trap.S
+ * @ingroup stack_trace
+
+ * @brief this is a function that is called by a custom trap handler to perform
+ *        a backtrace
+ */
+
+	.text                                                                   
+	.align 4                                                                
+	.globl  trace_trap
+
+#define PSR_ET      0x00000020         /* enable traps field         */
+
+trace_trap:
+	/* re-enable traps */
+	rd %psr, %l0
+	wr %l0, PSR_ET,%psr
+	nop
+	nop
+	nop
+
+	/* %sp of trapped function */
+	mov %fp, %o0
+
+	/* %pc of trapped function is stored in %l1 */
+	mov %l1, %o1
+	call trace
+	 nop
+	call die
+	 nop
+/* if we wanted to return from this: */
+#	jmp  %l1
+#	rett %l2
diff --git a/arch/sparc/mm/compile.sh b/arch/sparc/mm/compile.sh
new file mode 100644
index 0000000000000000000000000000000000000000..d37afae34e2b6834f20f558a35a5e2d6a33eaa93
--- /dev/null
+++ b/arch/sparc/mm/compile.sh
@@ -0,0 +1 @@
+sparc-elf-gcc *.c -I../include -I../../../include -I../../../include/kernel
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
new file mode 100644
index 0000000000000000000000000000000000000000..d13ad4d4995918b80dd5fc6b620a69197079f4c4
--- /dev/null
+++ b/arch/sparc/mm/srmmu.c
@@ -0,0 +1,1193 @@
+/**
+ *
+ * @file arch/sparc/mm/srmmu.c
+ *
+ *
+ *
+ * ## Overview
+ *
+ * This implements SRMMU functionality
+ *
+ * ## Mode of Operation
+ *
+ * This is how the srmmu context table works:
+ *
+ * "level 0" is the context pointer that is used by the MMU to determine which
+ * table to use, if a certain context is selected via srmmu_set_ctx()
+ * Since level 0 corresponds to a 4 GiB page mapping, it is also possible to
+ * use it as a page table entry, i.e. to transparently map the whole 32bit
+ * address through the MMU, you can use the context table pointer (ctp) as
+ * page table entry (pte):
+ *
+ *	srmmu_set_ctx_tbl_addr((unsigned long) _mmu_ctp);
+ *	_mmu_ctp[0] = SRMMU_PTE(0x0, (SRMMU_CACHEABLE | SRMMU_ACC_S_RWX_2));
+ *	srmmu_set_ctx(0);
+ *
+ * so the virtual address mapping starts at physical address address 0x0.
+ * Note: accessing memory regions not actually mapped in hardware will
+ * subsequently raise data access exceptions!
+ *
+ *
+ * If you are only interested in protecting larger chunks of memory, i.e.
+ * against accidential R/W/X operations, you can point a level 0 ctp to a
+ * level 1 table by setting it up as a page table descriptor (ptd):
+ *
+ *	_mmu_ctp[0] = SRMMU_PTD((unsigned long) &ctx->tbl.lvl1[0]);
+ *
+ * Let's say you want to map a 16 MiB chunk starting at 0x40000000
+ * transparently through the MMU, you would then set lvl 1 table entry
+ * 0x40 (64), since lvl1 tables addresses are formed by the two highest-order
+ * bytes:
+ *	ctx->tbl.lvl1[0x40] =
+ *		SRMMU_PTE(0x40000000, (SRMMU_CACHEABLE | SRMMU_ACC_S_RWX_2));
+ *
+ * an then select the MMU context.
+ *
+ * From the above example, you can already guess how the MMU context tables
+ * work. If you access address 0x40000000, the MMU will take the two highest
+ * order bytes and use them as an offset into the level 1 table. If it finds
+ * that the entry is marked as pte, it takes the corresponding two highest-order
+ * address bytes in the entry and replaces the bytes in your address with those
+ * bytes.
+ *
+ * Say you try to access 0x40123456, the MMU will strip 0x40 and look into the
+ * table, where (in our case) finds the referenced physical address bytes to be
+ * 0x40 as well, and will hence take your 6 lower order bytes 0x123456 and put
+ * 0x40 in front again, resulting in the translated address 0x40123456.
+ *
+ * Let's say you created a second mapping to the same physical address:
+ *
+ *	ctx->tbl.lvl1[0xaa] =
+ *		SRMMU_PTE(0x40000000, (SRMMU_CACHEABLE | SRMMU_ACC_S_RWX_2));
+ *
+ * and try to access 0xaa123456. The MMU will then replace 0xaa with 0x40, and
+ * you'll get the same physical address again!
+ *
+ * Note: You could actually use this to map flat physical memory into a
+ * virtual "circular" topology by repeating the mapping for a range of virtual
+ * address space and use an automatically underflowing buffer index
+ * (i.e. a 8 or 16 bit integer type).
+ *
+ * Now, if you wanted to map a 16 MiB chunk into smaller sections, rather than
+ * setting up the entry as a pte, you would configure it as a ptd that
+ * references a lvl 2 table, e.g.
+ *
+ *	ctx->tbl.lvl1[0x40] = SRMMU_PTD((unsigned long) &ctx->tbl->lvl2[0]);
+ *
+ * and then set up the lvl 2 table entries, which reference 64 chunks of 256 KiB
+ * each, which means that the next 6 _bits_ in the address are used by the MMU
+ * for the offset into the table, i.e. (addr & 0x00FC0000 ) >> 18
+ *
+ * If you configure a mapping for 0xaa04xxxx, you would set:
+ *	ctx->tbl.lvl2[0xaa][0x01] =
+ *		SRMMU_PTE(0x40000000, (SRMMU_CACHEABLE | SRMMU_ACC_S_RWX_2));
+ *
+ * and then access 0xaa04cdef, the MMU will strip the upper 14 bits from that
+ * address and replace them with the upper 14 bits of 0x40000000 and hence
+ * reference a physical address of 0x4000cdef
+ *
+ * The same applies to level 3 contexts, which are 4 kiB chunks, so here the
+ * MMU replaces the upper 20 _bits_ of your address.
+ *
+ *
+ * @note Unused context table entries must be set accordingly, otherwise
+ * the MMU might try to establish a mapping, so initialise all context table
+ * entries to SRMMU_ENTRY_TYPE_INVALID.
+ *
+ *
+ * ## Error Handling
+ *
+ * TODO
+ *
+ * ## Notes
+ *
+ * We only allow large (16 MiB) and small (4 kiB) page mappings. The intermediate
+ * (256 kiB) level is always explcitly mapped via small pages.
+ *
+ * The level 1 context table is always allocated, level 2 and 3 tables are not
+ * and are allocated/released as needed. The allocation is done via a low-level
+ * allocator, that is specified via the srmmu_init_ctx() call and may be
+ * different for each context in the context list. The alloc() call is expected
+ * to either return a valid buffer of at least the size requested, or NULL on
+ * error.
+ *
+ * If a mapping is released, all allocations are released by walking the tree,
+ * since we don't track them separately. If it turns out that this is done
+ * often, it might for performance reasons be prefereable to maintain SLABS of
+ * page tables, i.e. one per mapping size, where we take and return them as
+ * needed. We could also track them of course...
+ *
+ *
+ * @todo this needs some cleanup/streamlining/simplification
+ */
+
+
+#include <kernel/kernel.h>
+#include <kernel/printk.h>
+#include <list.h>
+
+#include <page.h>
+#include <mm.h>
+#include <srmmu.h>
+#include <srmmu_access.h>
+#include <errno.h>
+
+
+
+static unsigned long *_mmu_ctp;	      /* table of mmu context table pointers */
+static unsigned long  _mmu_num_ctp;   /* number of contexts in the ctp       */
+static struct mmu_ctx *_mmu_ctx;      /* the currently configured context    */
+
+
+
+/**
+ * The magic entry is placed one word prior to the start of the aligned
+ * address. If the allocation address coincides with the aligned address,
+ * a magic marker is placed in the word following the end of the table.
+ * since the offset is at most (SRMMU_TBL_LVL_1_ALIGN - 1), this is the
+ * mask that is used for the _negative_ offset to the allocated address.
+ *
+ * @note we can remove this once we have chunk_alloc_aligned() (which will
+ * subsequently require a function bootmem_alloc_aligned())
+ *
+ * @note the above needs another solution to detect empty subtables
+ */
+
+/* XXX: it works for now, but this really needs some improvement, at least
+ *	a checkbit or so...
+ */
+#define MAGIC_MARKER (0xdeadda7a & ~(SRMMU_TBL_LVL_1_ALIGN - 1))
+#define MAGIC(offset) (((offset) & (SRMMU_TBL_LVL_1_ALIGN - 1)) | MAGIC_MARKER)
+#define OFFSET_FROM_MAGIC(x) ((x) & (SRMMU_TBL_LVL_1_ALIGN - 1))
+#define IS_MAGIC(x) (((x) & ~(SRMMU_TBL_LVL_1_ALIGN - 1)) == MAGIC_MARKER)
+
+#define MAGIC_REF_CNT(x) (x)
+#define REF_FROM_MAGIC(x) (x)
+
+#define MAGIC_WORDS 2
+
+/**
+ * table level lvl1 is always SRMMU_SIZE_TBL_LVL_1 entries
+ * depending on the number of forward references (ptd's) into the
+ * higher level tables, the following applies:
+ *
+ * lvl2: at most SRMMU_SIZE_TBL_LVL_1 * SRMMU_SIZE_TBL_LVL_2
+ * lvl3: at most n_lvl2 * SRMMU_SIZE_TBL_LVL_3
+ *
+ */
+
+struct mmu_ctx_tbl {
+
+	struct srmmu_ptde *lvl1;
+
+	struct srmmu_ptde *lvl2[SRMMU_SIZE_TBL_LVL_1][SRMMU_SIZE_TBL_LVL_2];
+	struct srmmu_ptde *lvl3[SRMMU_SIZE_TBL_LVL_1][SRMMU_SIZE_TBL_LVL_2];
+
+
+	unsigned long n_lvl2;
+	unsigned long n_lvl3;
+};
+
+struct mmu_ctx {
+	struct mmu_ctx_tbl tbl;
+
+	unsigned int ctx_num;
+
+	void *(*alloc)(size_t size);
+	void  (*free) (void *addr);
+
+
+	/* lower and upper boundary of unusable memory space */
+	unsigned long lo_res;
+	unsigned long hi_res;
+
+	struct list_head node;
+};
+
+
+static struct list_head ctx_free;
+static struct list_head ctx_used;
+
+
+
+
+
+static inline void del_mmu_ctx_from_list(struct mmu_ctx *ctx)
+{
+	list_del(&ctx->node);
+}
+
+static inline void add_mmu_ctx_to_list(struct mmu_ctx *ctx,
+				       struct list_head *list)
+{
+	list_add_tail(&ctx->node, list);
+}
+
+static inline void mmu_ctx_add_free(struct mmu_ctx *ctx)
+{
+	list_add_tail(&ctx->node, &ctx_free);
+}
+
+static inline void mmu_ctx_add_used(struct mmu_ctx *ctx)
+{
+	list_add_tail(&ctx->node, &ctx_used);
+}
+
+static inline void mmu_ctx_move_free(struct mmu_ctx *ctx)
+{
+	list_move_tail(&ctx->node, &ctx_free);
+}
+
+static inline void mmu_ctx_move_used(struct mmu_ctx *ctx)
+{
+	list_move_tail(&ctx->node, &ctx_used);
+}
+
+static inline void mmu_init_ctx_lists(void)
+{
+	INIT_LIST_HEAD(&ctx_free);
+	INIT_LIST_HEAD(&ctx_used);
+}
+
+static inline struct mmu_ctx *mmu_find_ctx(unsigned int ctx_num)
+{
+	struct mmu_ctx *p_elem;
+
+
+	if (ctx_num > _mmu_num_ctp)
+		return NULL;
+
+	list_for_each_entry(p_elem, &ctx_used, node) {
+		if (p_elem->ctx_num == ctx_num);
+			return p_elem;
+	}
+
+	list_for_each_entry(p_elem, &ctx_free, node) {
+		if (p_elem->ctx_num == ctx_num);
+			return p_elem;
+	}
+
+
+	return NULL;
+}
+
+
+/**
+ * @brief set the current working context
+ */
+
+static inline int mmu_set_current_ctx(unsigned int ctx_num)
+{
+	struct mmu_ctx *ctx;
+
+	ctx = mmu_find_ctx(ctx_num);
+	if (!ctx)
+		return -EINVAL;
+
+	_mmu_ctx = ctx;
+
+	return 0;
+}
+
+
+/**
+ * @brief get the current working context
+ */
+
+static inline struct mmu_ctx *mmu_get_current_ctx(void)
+{
+	return _mmu_ctx;
+}
+
+
+/**
+ * @brief add a new working context
+ */
+
+static inline void mmu_add_ctx(unsigned long ptd)
+{
+	_mmu_ctp[_mmu_num_ctp] = ptd;
+	_mmu_num_ctp++;
+}
+
+/**
+ * @brief get the current number of registered contexts
+ *
+ * @returns the current number of registered contexts
+ */
+
+static inline unsigned int mmu_get_num_ctx(void)
+{
+	return _mmu_num_ctp;
+}
+
+/**
+ * @brief set the context table pointer
+ * @param addr a pointer to the table
+ */
+
+static inline void mmu_set_ctp(unsigned long *addr)
+{
+	_mmu_ctp = addr;
+	srmmu_set_ctx_tbl_addr((unsigned long) _mmu_ctp);
+}
+
+
+/**
+ * @brief 1:1 map the full 32 bit space
+ *
+ * @param ctx_num the context number to configure
+ */
+
+static inline void mmu_set_map_full(unsigned int ctx_num)
+{
+	if (ctx_num >= SRMMU_CONTEXTS)
+		return;
+
+	_mmu_ctp[ctx_num] = SRMMU_PTE(0x0,
+				      (SRMMU_CACHEABLE | SRMMU_ACC_S_RWX_2));
+}
+
+
+/**
+ * @brief initialise all lvl1 table entries as invalid
+ */
+
+static void mmu_set_lvl1_tbl_invalid(struct srmmu_ptde *ptde)
+{
+	int i;
+
+	for (i = 0; i < SRMMU_SIZE_TBL_LVL_1; i++)
+	       ptde[i].pte = SRMMU_ENTRY_TYPE_INVALID;
+}
+
+
+/**
+ * @brief initialise all lvl2 table entries as invalid
+ */
+
+static void mmu_set_lvl2_tbl_invalid(struct srmmu_ptde *ptde)
+{
+	int i;
+
+	for (i = 0; i < SRMMU_SIZE_TBL_LVL_2; i++)
+	       ptde[i].pte = SRMMU_ENTRY_TYPE_INVALID;
+}
+
+
+/**
+ * @brief initialise all lvl3 table entries as invalid
+ */
+
+static void mmu_set_lvl3_tbl_invalid(struct srmmu_ptde *ptde)
+{
+	int i;
+
+	for (i = 0; i < SRMMU_SIZE_TBL_LVL_3; i++)
+	       ptde[i].pte = SRMMU_ENTRY_TYPE_INVALID;
+}
+
+
+/**
+ * @brief increment page reference counter
+ *
+ * @returns current reference count
+ */
+
+static unsigned long mmu_inc_ref_cnt(struct srmmu_ptde *tbl,
+					unsigned long tbl_size)
+{
+	unsigned long cnt;
+
+	unsigned long *ptd;
+
+
+	if (IS_MAGIC(tbl[-1].ptd))
+		ptd = &tbl[-2].ptd;
+	else if (IS_MAGIC(tbl[tbl_size].ptd))
+		ptd = &tbl[tbl_size + 1].ptd;
+	else
+		BUG();
+
+
+	cnt = REF_FROM_MAGIC((*ptd)) + 1;
+	(*ptd) = MAGIC_REF_CNT(cnt);
+
+	return cnt;
+}
+
+
+/**
+ * @brief decrement page reference counter
+ *
+ * @returns current reference count
+ */
+
+static unsigned long mmu_dec_ref_cnt(struct srmmu_ptde *tbl,
+					unsigned long tbl_size)
+{
+	unsigned long cnt;
+
+	unsigned long *ptd;
+
+
+	if (IS_MAGIC(tbl[-1].ptd))
+		ptd = &tbl[-2].ptd;
+	else if (IS_MAGIC(tbl[tbl_size].ptd))
+		ptd = &tbl[tbl_size + 1].ptd;
+	else
+		BUG();
+
+
+	cnt = REF_FROM_MAGIC((*ptd));
+	if (!cnt)
+		BUG();
+
+	cnt = cnt - 1;
+
+	(*ptd) = MAGIC_REF_CNT(cnt);
+
+	return cnt;
+}
+
+
+/**
+ * @brief allocate and align a SRMMU page table
+ */
+
+static struct srmmu_ptde *mmu_alloc_tbl(struct mmu_ctx *ctx,
+					unsigned long tbl_size)
+{
+	int offset;
+
+	struct srmmu_ptde *ptde;
+	struct srmmu_ptde *ptde_align;
+
+
+	ptde = (struct srmmu_ptde *) ctx->alloc(2 * tbl_size);
+	if (!ptde)
+		return NULL;
+
+	ptde_align = ALIGN_PTR(ptde, tbl_size);
+
+	/* store positive offset as a ptd */
+	offset = (int) ptde_align - (int) ptde;
+	if (offset > MAGIC_WORDS) {
+		ptde_align[-1].ptd = MAGIC(offset);
+		ptde_align[-2].ptd = MAGIC_REF_CNT(0);
+	} else {
+		ptde_align[tbl_size].ptd = MAGIC(0);
+		ptde_align[tbl_size + 1].ptd = MAGIC_REF_CNT(0);
+	}
+
+	return ptde_align;
+}
+
+
+/**
+ * @brief free a table
+ */
+
+static void mmu_free_tbl(struct mmu_ctx *ctx, struct srmmu_ptde *tbl,
+			 unsigned long tbl_size)
+{
+	unsigned long ptd;
+
+	void *addr;
+
+
+	if (IS_MAGIC(tbl[-1].ptd))
+		ptd = tbl[-1].ptd;
+	else if (IS_MAGIC(tbl[tbl_size].ptd))
+		ptd = tbl[tbl_size].ptd;
+	else
+		BUG();
+
+	addr = (void *) ((int) tbl - OFFSET_FROM_MAGIC(ptd));
+
+	ctx->free(addr);
+}
+
+/**
+ * @brief allocate a level 1 table
+ */
+
+static struct srmmu_ptde *mmu_alloc_lvl1_tbl(struct mmu_ctx *ctx)
+{
+	struct srmmu_ptde *ptde;
+
+
+	ptde = mmu_alloc_tbl(ctx, SRMMU_TBL_LVL_1_ALIGN);
+	if (!ptde)
+		return NULL;
+
+	mmu_set_lvl1_tbl_invalid(ptde);
+
+	return ptde;
+}
+
+
+/**
+ * @brief look up a level 2 table by virtual address
+ */
+
+static struct srmmu_ptde *mmu_find_tbl_lvl2(struct mmu_ctx *ctx,
+					    unsigned long va)
+{
+	unsigned long page;
+
+	struct srmmu_ptde *lvl1;
+
+
+	lvl1 = ctx->tbl.lvl1;
+
+	page = SRMMU_LVL1_GET_TBL_OFFSET(va);
+
+	if (lvl1[page].pte & SRMMU_ENTRY_TYPE_PT_ENTRY)
+		return NULL;
+
+	if (lvl1[page].pte == SRMMU_ENTRY_TYPE_INVALID)
+		return NULL;
+
+	return (struct srmmu_ptde *) SRMMU_PTD_TO_ADDR(lvl1[page].ptd);
+}
+
+
+/**
+ * @brief look up a level 3 table by virtual address
+ */
+
+static struct srmmu_ptde *mmu_find_tbl_lvl3(struct mmu_ctx *ctx,
+					    unsigned long va)
+{
+	unsigned long page;
+
+	struct srmmu_ptde *lvl2;
+
+
+	lvl2 = mmu_find_tbl_lvl2(ctx, va);
+	if (!lvl2)
+		goto no_table;
+
+	page = SRMMU_LVL2_GET_TBL_OFFSET(va);
+
+	if (lvl2[page].pte & SRMMU_ENTRY_TYPE_PT_ENTRY)
+		goto no_table;
+
+	if (lvl2[page].pte == SRMMU_ENTRY_TYPE_INVALID)
+		goto no_table;
+
+
+	return (struct srmmu_ptde *) SRMMU_PTD_TO_ADDR(lvl2[page].ptd);
+
+no_table:
+	return NULL;
+}
+
+
+/**
+ * @brief if necessary, allocate a new level 2 table
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+static int mmu_need_lvl2_tbl(struct mmu_ctx *ctx, unsigned long va)
+{
+	unsigned long pg_lvl1;
+
+	struct srmmu_ptde *ptde;
+
+
+	pg_lvl1 = SRMMU_LVL1_GET_TBL_OFFSET(va);
+
+	/* lvl 1 page in use, no can do */
+	if (ctx->tbl.lvl1[pg_lvl1].pte & SRMMU_ENTRY_TYPE_PT_ENTRY)
+		return -EINVAL;
+
+	if (ctx->tbl.lvl1[pg_lvl1].pte != SRMMU_ENTRY_TYPE_INVALID)
+		return 0;
+
+
+	ptde = mmu_alloc_tbl(ctx, SRMMU_TBL_LVL_2_ALIGN);
+	if (!ptde)
+		return -ENOMEM;
+
+	mmu_set_lvl2_tbl_invalid(ptde);
+
+	/* point entry to the new lvl2 table */
+	ctx->tbl.lvl1[pg_lvl1].ptd = SRMMU_PTD((unsigned long) ptde);
+
+	/* we don't count lvl 2 references in lvl1 tables */
+
+	pr_debug("SRMMU: allocated new lvl2 table\n");
+
+	return 0;
+}
+
+
+/**
+ * @brief if necessary, allocate a new level 2 table
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+static int mmu_need_lvl3_tbl(struct mmu_ctx *ctx, unsigned long va)
+{
+	int ret;
+
+	unsigned long pg_lvl2;
+
+	struct srmmu_ptde *lvl2;
+	struct srmmu_ptde *lvl3;
+
+
+	/* see if we need a lvl3 table */
+	lvl3 = mmu_find_tbl_lvl3(ctx, va);
+	if (lvl3)
+		return 0;
+
+	/* we might need a lvl2 table first */
+	lvl2 = mmu_find_tbl_lvl2(ctx, va);
+	if (!lvl2) {
+		if((ret = mmu_need_lvl2_tbl(ctx, va)))
+			return ret;
+
+		lvl2 = mmu_find_tbl_lvl2(ctx, va);
+	}
+
+	/* allocate a lvl3 table */
+	lvl3 = mmu_alloc_tbl(ctx, SRMMU_TBL_LVL_3_ALIGN);
+	if (!lvl3)
+		return -ENOMEM;
+
+	mmu_set_lvl3_tbl_invalid(lvl3);
+
+	/* point lvl2 entry to the new lvl3 table */
+	pg_lvl2 = SRMMU_LVL2_GET_TBL_OFFSET(va);
+	lvl2[pg_lvl2].ptd = SRMMU_PTD((unsigned long) lvl3);
+
+	mmu_inc_ref_cnt(lvl2, SRMMU_TBL_LVL_2_ALIGN);
+
+	pr_debug("SRMMU: allocated new lvl3 table\n");
+
+	return 0;
+}
+
+
+/**
+ * @brief create a new SRMMU context
+ *
+ * @param alloc a pointer to a function we can use to allocate MMU context
+ *	  tables
+ * @param free a pointer to a function that returns MMU context tables to
+ *	  the allocator
+ *
+ *
+ * @return >= 0: number of created context, otherwise error
+ *
+ * @note allows up to SRMMU_CONTEXTS
+ */
+
+int srmmu_new_ctx(void *(*alloc)(size_t size), void  (*free)(void *addr))
+{
+	struct mmu_ctx *ctx;
+
+
+	if (mmu_get_num_ctx() > SRMMU_CONTEXTS)
+		return -ENOMEM;
+
+	ctx = (struct mmu_ctx *) alloc(sizeof(struct mmu_ctx));
+	if (!ctx)
+		return -ENOMEM;
+
+
+	ctx->alloc = alloc;
+	ctx->free  = free;
+
+	ctx->tbl.lvl1 = mmu_alloc_lvl1_tbl(ctx);
+	if (!ctx->tbl.lvl1) {
+		free(ctx);
+		return -ENOMEM;
+	}
+
+	ctx->ctx_num = mmu_get_num_ctx();
+
+	mmu_add_ctx(SRMMU_PTD((unsigned long) &ctx->tbl.lvl1[0]));
+
+	mmu_ctx_add_free(ctx);
+
+	return ctx->ctx_num;
+}
+
+
+/**
+ * @brief release lvl3 pages
+ *
+ * @returns 1 if lvl 3 directory still exists, 0 if it was released
+ */
+
+static int srmmu_release_lvl3_pages(struct mmu_ctx *ctx,
+				    unsigned long va, unsigned long va_end,
+				    void (*free_page)(void *addr))
+{
+	unsigned long page;
+
+	struct srmmu_ptde *lvl3;
+
+
+
+	lvl3 = mmu_find_tbl_lvl3(ctx, va);
+
+	if ((va + SRMMU_SIZE_TBL_LVL_3 * SRMMU_SMALL_PAGE_SIZE ) < va_end)
+		va_end = va + SRMMU_SIZE_TBL_LVL_3 * SRMMU_SMALL_PAGE_SIZE;
+
+	for ( ; va < va_end; va += SRMMU_SMALL_PAGE_SIZE) {
+
+		page = SRMMU_LVL3_GET_TBL_OFFSET(va);
+
+		/* it is quite possible that this is not mapped */
+		if (lvl3[page].pte == SRMMU_ENTRY_TYPE_INVALID) {
+			pr_debug("SRMMU: tried to release address 0x%08x, but "
+				 "lvl3 page was marked invalid, ignoring.\n",
+				 va);
+			continue;
+		}
+
+		if (lvl3[page].pte & SRMMU_ENTRY_TYPE_PT_ENTRY) {
+
+			free_page((void *) SRMMU_PTE_TO_ADDR(lvl3[page].pte));
+
+			pr_debug("SRMMU: freed physical page %lx\n",
+				 SRMMU_PTE_TO_ADDR(lvl3[page].pte));
+
+			lvl3[page].pte = SRMMU_ENTRY_TYPE_INVALID;
+
+			if (!mmu_dec_ref_cnt(lvl3, SRMMU_TBL_LVL_3_ALIGN)) {
+				mmu_free_tbl(ctx, lvl3, SRMMU_TBL_LVL_3_ALIGN);
+				pr_debug("SRMMU: released lvl3 table\n");
+
+				return 0;
+			}
+		}
+
+
+	}
+
+	return 1;
+}
+
+
+/**
+ * @brief recursively release lvl2 pages
+ *
+ * @returns 1 if lvl 2 directory still exists, 0 if it was released
+ */
+
+static int srmmu_release_lvl2_pages(struct mmu_ctx *ctx,
+				     unsigned long va, unsigned long va_end,
+				     void (*free_page)(void *addr))
+{
+	unsigned long page;
+
+	struct srmmu_ptde *lvl2;
+
+
+	lvl2 = mmu_find_tbl_lvl2(ctx, va);
+
+	if ((va + SRMMU_SIZE_TBL_LVL_2 * SRMMU_MEDIUM_PAGE_SIZE ) < va_end)
+		va_end = va + SRMMU_SIZE_TBL_LVL_2 * SRMMU_MEDIUM_PAGE_SIZE;
+
+	for ( ; va < va_end; va += SRMMU_MEDIUM_PAGE_SIZE) {
+
+		page = SRMMU_LVL2_GET_TBL_OFFSET(va);
+
+		/* medium mapping not used, its all done via small pages */
+
+		/* it is quite possible that this is not mapped */
+		if (lvl2[page].pte == SRMMU_ENTRY_TYPE_INVALID) {
+			pr_debug("SRMMU: tried to release address 0x%08x, but "
+				 "lvl2 page was marked invalid, ignoring.\n",
+				 va);
+			continue;
+		}
+
+		if (!srmmu_release_lvl3_pages(ctx, va, va_end, free_page)) {
+			lvl2[page].pte = SRMMU_ENTRY_TYPE_INVALID;
+
+			pr_debug("SRMMU: lvl3 table unreferenced\n");
+
+
+			/* no more subtables, free ourselves */
+			if (!mmu_dec_ref_cnt(lvl2, SRMMU_TBL_LVL_2_ALIGN)) {
+				mmu_free_tbl(ctx, lvl2, SRMMU_TBL_LVL_2_ALIGN);
+				pr_debug("SRMMU: released lvl2 table\n");
+
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+/**
+ * @brief recursively release lvl1 pages
+ */
+
+static void srmmu_release_lvl1_pages(struct mmu_ctx *ctx,
+				     unsigned long va, unsigned long va_end,
+				     void (*free_page)(void *addr))
+{
+
+	unsigned long page;
+
+	struct srmmu_ptde *lvl1;
+
+
+	lvl1 = ctx->tbl.lvl1;
+
+	for ( ; va < va_end; va += SRMMU_LARGE_PAGE_SIZE) {
+
+		page = SRMMU_LVL1_GET_TBL_OFFSET(va);
+
+		/* large mapping */
+		if (lvl1[page].pte & SRMMU_ENTRY_TYPE_PT_ENTRY) {
+			free_page((void *) SRMMU_PTE_TO_ADDR(lvl1[page].pte));
+			lvl1[page].pte = SRMMU_ENTRY_TYPE_INVALID;
+			continue;
+		}
+
+		if (lvl1[page].pte == SRMMU_ENTRY_TYPE_INVALID) {
+			pr_debug("SRMMU: tried to release address 0x%08x, but "
+				  "lvl1 page was marked invalid, skipping.\n",
+				  va);
+			continue;
+		}
+
+		if (!srmmu_release_lvl2_pages(ctx, va, va_end, free_page)) {
+			lvl1[page].pte = SRMMU_ENTRY_TYPE_INVALID;
+			pr_debug("SRMMU: lvl2 table unreferenced\n");
+		}
+	}
+}
+
+
+/**
+ * @brief recursively release pages by address
+ *
+ * @param ctx_num the context number
+ * @param va the start virtual address
+ * @param va_end the end virtual address
+ * @parma free_page a function pages are returned to
+ *
+ * @note the addresses are assumed aligned to the page size
+ */
+
+void srmmu_release_pages(unsigned long ctx_num,
+			 unsigned long va, unsigned long va_end,
+			 void  (*free_page)(void *addr))
+{
+	struct mmu_ctx *ctx;
+
+	if (va_end <= va)
+		return;
+
+	ctx = mmu_find_ctx(ctx_num);
+
+	srmmu_release_lvl1_pages(ctx, va, va_end, free_page);
+}
+
+
+/**
+ * @brief map a 16 MiB page from a virtual address to a physical address
+ *
+ * @param ctx_num the context number to do the mapping in
+ * @param va the virtual address
+ * @param pa the physical address
+ * @parma perm the permissions to configure for the page
+ *
+ * @note the addresses are assumed to be aligned to the requested page size
+ *
+ * @return 0 on success
+ */
+
+int srmmu_do_large_mapping(unsigned long ctx_num,
+			   unsigned long va, unsigned long pa,
+			   unsigned long perm)
+{
+	size_t page;
+
+	struct mmu_ctx *ctx;
+
+
+	if (mmu_get_num_ctx() < ctx_num)
+		return -EINVAL;
+
+
+	ctx = mmu_find_ctx(ctx_num);
+
+	if (!ctx)
+		return -ENOMEM;
+
+	page = SRMMU_LVL1_GET_TBL_OFFSET(va);
+	ctx->tbl.lvl1[page].pte = SRMMU_PTE(pa, perm);
+
+	leon_flush_cache_all();
+	leon_flush_tlb_all();
+
+	pr_debug("SRMMU: mapped 16 MiB page from 0x%08x to 0x%08x "
+		 "of context %d, permissions 0x%08x\n",
+		 va, pa, ctx->ctx_num, perm);
+
+	return 0;
+}
+
+
+/**
+ * @brief map a virtual address range to a physical address range in 16MiB pages
+ *
+ * @param ctx_num the context number to do the mapping in
+ * @param va the virtual address
+ * @param pa the physical address
+ * @param pages the number of SRMMU_LARGE_PAGE_SIZE pages
+ * @parma perm the permissions to configure for the pages
+ *
+ * @return 0 on success
+ */
+
+int srmmu_do_large_mapping_range(unsigned long ctx_num,
+				 unsigned long va, unsigned long pa,
+				 unsigned long num_pages, unsigned long perm)
+{
+	int ret;
+
+	unsigned long i;
+
+
+	if (mmu_get_num_ctx() < ctx_num)
+		return -EINVAL;
+
+
+	for (i = 0; i < num_pages; i++) {
+		ret = srmmu_do_large_mapping(ctx_num,
+					     va + i * SRMMU_LARGE_PAGE_SIZE,
+					     pa + i * SRMMU_LARGE_PAGE_SIZE,
+					     perm);
+		if (ret) {
+			pr_crit("SRMMU: failed to map %d pages from "
+				"[0x%08lx, 0x%08lx] to [0x%08lx, 0x%08lx]\n",
+				(num_pages - i),
+				va + i * SRMMU_LARGE_PAGE_SIZE,
+				va + num_pages * SRMMU_LARGE_PAGE_SIZE,
+				pa + i * SRMMU_LARGE_PAGE_SIZE,
+				pa + num_pages * SRMMU_LARGE_PAGE_SIZE);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * @brief map a 4 kiB page from a virtual address to a physical address
+ *
+ * @param ctx_num the context number to do the mapping in
+ * @param va the virtual address
+ * @param pa the physical address
+ * @parma perm the permissions to configure for the page
+ *
+ * @note the addresses are assumed to be aligned to the requested page size
+ *
+ * @return 0 on success
+ */
+
+int srmmu_do_small_mapping(unsigned long ctx_num,
+			   unsigned long va, unsigned long pa,
+			   unsigned long perm)
+{
+	int ret;
+
+	unsigned long pg;
+	struct mmu_ctx *ctx;
+	struct srmmu_ptde *lvl3;
+
+
+
+	if (mmu_get_num_ctx() < ctx_num)
+		return -EINVAL;
+
+	ctx = mmu_find_ctx(ctx_num);
+
+	if (!ctx)
+		return -ENOMEM;
+
+
+	ret = mmu_need_lvl3_tbl(ctx, va);
+	if (ret)
+		return ret;
+
+	lvl3 = mmu_find_tbl_lvl3(ctx, va);
+
+	pg = SRMMU_LVL3_GET_TBL_OFFSET(va);
+
+	lvl3[pg].pte = SRMMU_PTE(pa, perm);
+
+	mmu_inc_ref_cnt(lvl3, SRMMU_TBL_LVL_3_ALIGN);
+
+	leon_flush_cache_all();
+	leon_flush_tlb_all();
+
+	pr_debug("SRMMU: mapped 4 kiB page from 0x%08x to 0x%08x "
+		 "of context %d, permissions 0x%08x\n",
+		 va, pa, ctx->ctx_num, perm);
+
+	return 0;
+}
+
+/**
+ * @brief map a virtual address range to a physical address range in 4kiB pages
+ *
+ * @param ctx_num the context number to do the mapping in
+ * @param va the virtual address
+ * @param pa the physical address
+ * @param pages the number of SRMMU_SMALL_PAGE_SIZE pages
+ * @parma perm the permissions to configure for the pages
+ *
+ * @return 0 on success
+ */
+
+int srmmu_do_small_mapping_range(unsigned long ctx_num,
+				 unsigned long va, unsigned long pa,
+				 unsigned long num_pages, unsigned long perm)
+{
+	int ret;
+
+	unsigned long i;
+
+
+	if (mmu_get_num_ctx() < ctx_num)
+		return -EINVAL;
+
+
+	for (i = 0; i < num_pages; i++) {
+		ret = srmmu_do_small_mapping(ctx_num,
+					     va + i * SRMMU_SMALL_PAGE_SIZE,
+					     pa + i * SRMMU_SMALL_PAGE_SIZE,
+					     perm);
+		if (ret) {
+			pr_crit("SRMMU: failed to map %d pages from "
+				"[0x%08lx, 0x%08lx] to [0x%08lx, 0x%08lx]\n",
+				(num_pages - i),
+				va + i * SRMMU_SMALL_PAGE_SIZE,
+				va + num_pages * PAGE_SIZE,
+				pa + i * SRMMU_SMALL_PAGE_SIZE,
+				pa + num_pages * PAGE_SIZE);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * @brief select a MMU context
+ *
+ * @param ctx_num the context number to select
+ *
+ * @return 0 on success, otherwise error
+ */
+
+int srmmu_select_ctx(unsigned long ctx_num)
+{
+	if (mmu_get_num_ctx() < ctx_num)
+		return -EINVAL;
+
+	if(mmu_set_current_ctx(ctx_num))
+		return -EINVAL;
+
+	srmmu_set_ctx(ctx_num);
+
+	leon_flush_cache_all();
+	leon_flush_tlb_all();
+
+	return 0;
+}
+
+
+/**
+ * @brief enable MMU operation
+ */
+
+void srmmu_enable_mmu(void)
+{
+	srmmu_set_mmureg(0x00000001);
+	leon_flush_cache_all();
+}
+
+
+/**
+ * @brief basic initialisation of the MMU
+ *
+ * @param alloc a pointer to a function we can use to allocate MMU context
+ *	  tables
+ * @param free a pointer to a function that returns MMU context tables to
+ *	  the allocator
+ *
+ * @return 0 on success, otherwise error
+ *
+ * @note requires at least one mapping and a call to srmmu_enable_mmu()
+ *	 to function
+ */
+
+int srmmu_init(void *(*alloc)(size_t size), void  (*free)(void *addr))
+{
+	int ret;
+
+	unsigned int i;
+
+	unsigned long *ctp;
+
+	struct mmu_ctx *ctx;
+
+	const size_t ctp_size = SRMMU_CONTEXTS * sizeof(unsigned long);
+
+
+	/* don't call this twice by accident, we don't support multiple
+	 * context table pointer tables
+	 */
+	BMP();
+
+	mmu_init_ctx_lists();
+
+	/* allocate twice the size of the table, so we can align it to the
+	 * a boundary equal its own size
+	 */
+	ctp = (unsigned long *) alloc(2 * ctp_size);
+	if (!ctp)
+		return -ENOMEM;
+
+	ctp = ALIGN_PTR(ctp, ctp_size);
+
+	mmu_set_ctp(ctp);
+
+	/* all contexts are invalid by default */
+	for (i = 0; i < SRMMU_CONTEXTS; i++)
+		ctp[i] = SRMMU_ENTRY_TYPE_INVALID;
+
+
+	ret = srmmu_new_ctx(alloc, free);
+	if(ret)
+		return ret;
+
+	BUG_ON(mmu_set_current_ctx(0));
+
+	ctx = mmu_get_current_ctx();
+
+	srmmu_select_ctx(0);
+
+	return 0;
+}
diff --git a/arch/sparc/mm/srmmu_access.c b/arch/sparc/mm/srmmu_access.c
new file mode 100644
index 0000000000000000000000000000000000000000..519f8d40aebd63b429153b8c7c02e4325bbf3e9d
--- /dev/null
+++ b/arch/sparc/mm/srmmu_access.c
@@ -0,0 +1,189 @@
+
+/**
+ * @brief SRMMU register access functions
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ *
+ * @note only LEON ASI is supported
+ */
+
+
+
+#include <uapi/asi.h>
+#include <srmmu.h>
+
+#include <kernel/kernel.h>
+
+
+#define MMU_CTRL_REG	0x00000000	/* control register */
+#define MMU_CTXP_REG	0x00000100	/* context pointer */
+#define MMU_CTX_REG	0x00000200	/* context */
+#define MMU_FLTS_REG	0x00000300	/* fault status */
+#define MMU_FLTA_REG	0x00000400	/* fault address */
+
+
+
+/**
+ * @brief access to the SRMMU control register
+ *
+ * @return SR MMU control register contents
+ */
+
+unsigned int srmmu_get_mmu_ctrl(void)
+{
+	unsigned int mmu_ctrl;
+
+	__asm__ __volatile__(
+			"lda	[%%g0] " __stringify(ASI_LEON_MMUREGS) ", %0\n\t"
+			: "=r" (mmu_ctrl)
+			:
+			:"memory");
+
+	return mmu_ctrl;
+}
+
+
+/**
+ * @brief access to the SRMMU fault status register
+ *
+ * @return SRMMU fault status register contents
+ */
+
+struct srmmu_fault_status srmmu_get_mmu_fault_status(void)
+{
+	struct srmmu_fault_status mmu_fault_status;
+
+	__asm__ __volatile__(
+			"lda	[%1] " __stringify(ASI_LEON_MMUREGS) ", %0\n\t"
+			:"=r" (mmu_fault_status)
+			:"r" (MMU_FLTS_REG)
+			:"memory");
+
+	return mmu_fault_status;
+}
+
+/**
+ * @brief access to the SRMMU fault address register
+ *
+ * @return SRMMU fault address register contents
+ */
+
+unsigned int srmmu_get_mmu_fault_address(void)
+{
+	unsigned int mmu_fault_addr;
+
+	__asm__ __volatile__(
+			"lda	[%1] " __stringify(ASI_LEON_MMUREGS) ", %0\n\t"
+			:"=r" (mmu_fault_addr)
+			:"r" (MMU_FLTA_REG)
+			:"memory");
+
+
+	return mmu_fault_addr;
+}
+
+
+/**
+ * @brief get the SRMMU implementation
+ *
+ * @return the implementation identifier
+ */
+
+unsigned int srmmu_get_mmu_impl(void)
+{
+	return (srmmu_get_mmu_ctrl() & SRMMU_CTRL_IMPL_MASK) >>
+		SRMMU_CTRL_IMPL_SHIFT;
+}
+
+
+/**
+ * @brief get the SRMMU implementation
+ *
+ * @return the implementation version
+ */
+
+unsigned int srmmu_get_mmu_ver(void)
+{
+	return (srmmu_get_mmu_ctrl() & SRMMU_CTRL_VER_MASK) >>
+		SRMMU_CTRL_VER_SHIFT;
+}
+
+
+/**
+ * @brief set the context table address in the MMU
+ *
+ * @param addr the address of the context table
+ *
+ * TODO: clean up magic
+ */
+
+void srmmu_set_ctx_tbl_addr(unsigned long addr)
+{
+	addr = ((addr >> 4) & 0xfffffff0);
+
+	__asm__ __volatile__("sta %0, [%1] %2\n\t"
+			     "flush          \n\t" : :
+			     "r" (addr), "r" (MMU_CTXP_REG),
+			     "i" (ASI_LEON_MMUREGS) :
+			     "memory");
+}
+
+
+/**
+ * @brief select the MMU contest
+ *
+ * @param ctx the context to select
+ *
+ * TODO: clean up magic
+ */
+
+void srmmu_set_ctx(unsigned int ctx)
+{
+	__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
+			     "r" (ctx), "r" (MMU_CTX_REG),
+			     "i" (ASI_LEON_MMUREGS) : "memory");
+}
+
+
+
+/**
+ * TODO
+ */
+
+void srmmu_set_mmureg(unsigned long regval)
+{
+	__asm__ __volatile__(
+			"sta	%0, [%%g0] " __stringify(ASI_LEON_MMUREGS) "\n\t"
+			:
+			: "r" (regval)
+			:"memory");
+
+}
+
+
+/**
+ * @brief flush all leon caches
+ 
+ * TODO: clean up magic, should be part of leon asm
+ */
+
+void leon_flush_cache_all(void)
+{
+#if 0	/* bug on MPPB leon 2? */
+	__asm__ __volatile__(" flush ");	/*iflush */
+	__asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
+			     "i"(ASI_LEON_DFLUSH) : "memory");
+#endif
+}
+
+
+/**
+ * @brief flush the the transation lookaside buffer
+ *
+ * TODO: clean up magic, should be part of leon asm
+ */
+void leon_flush_tlb_all(void)
+{
+	leon_flush_cache_all();
+	__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : "r"(0x400),
+			     "i"(ASI_LEON_MMUFLUSH) : "memory");
+}
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
new file mode 100644
index 0000000000000000000000000000000000000000..9e9cf3972678d8b1ecbe6e332826cbf74b8380d6
--- /dev/null
+++ b/include/asm-generic/io.h
@@ -0,0 +1,105 @@
+/**
+ * @file    include/asm-generic/io.h
+ *
+ * @copyright GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+* @brief a collection of accessor functions and macros to perform unbuffered
+ *        access to memory or hardware registers (generic variant)
+ *
+ * @note same conventions as linux kernel (see include/asm-generic/io.h)
+ *
+ * @todo since we really need only big endian functions for now, we don't do
+ *	 byte swaps (also we don't really need the functions in this file at
+ *	 this time)
+ */
+
+#ifndef _ASM_GENERIC_IO_H_
+#define _ASM_GENERIC_IO_H_
+
+#include <stdint.h>
+
+
+#ifndef __raw_readb
+#define __raw_readb __raw_readb
+static inline uint8_t __raw_readb(const volatile void *addr)
+{
+        return *(const volatile uint8_t *)addr;
+}
+#endif
+
+#ifndef __raw_readw
+#define __raw_readw __raw_readw
+static inline uint16_t __raw_readw(const volatile void *addr)
+{
+        return *(const volatile uint16_t *)addr;
+}
+#endif
+
+#ifndef __raw_readl
+#define __raw_readl __raw_readl
+static inline uint32_t __raw_readl(const volatile void *addr)
+{
+        return *(const volatile uint32_t *)addr;
+}
+#endif
+
+#ifndef __raw_writeb
+#define __raw_writeb __raw_writeb
+static inline void __raw_writeb(uint8_t w, volatile void *addr)
+{
+        *(volatile uint8_t *)addr = w;
+}
+#endif
+
+#ifndef __raw_writew
+#define __raw_writew __raw_writew
+static inline void __raw_writew(uint16_t w, volatile void *addr)
+{
+        *(volatile uint16_t *)addr = w;
+}
+#endif
+
+#ifndef __raw_writel
+#define __raw_writel __raw_writel
+static inline void __raw_writel(uint32_t l, volatile void *addr)
+{
+        *(volatile uint32_t *)addr = l;
+}
+#endif
+
+
+#ifndef ioread8
+#define ioread8(X)                      __raw_read8(X)
+#endif
+
+#ifndef iowrite8
+#define iowrite8(X)                     __raw_write8(X)
+#endif
+
+#ifndef ioread16be
+#define ioread16be(X)                   __raw_readw(X)
+#endif
+
+#ifndef ioread32be
+#define ioread32be(X)                   __raw_readl(X)
+#endif
+
+#ifndef iowrite16be
+#define iowrite16be(val,X)              __raw_writew(val,X)
+#endif
+
+#ifndef iowrite32be
+#define iowrite32be(val,X)              __raw_writel(val,X)
+#endif
+
+
+#endif
diff --git a/include/chunk.h b/include/chunk.h
new file mode 100644
index 0000000000000000000000000000000000000000..91474269300083b74e9ca38d6af9aa45c8309740
--- /dev/null
+++ b/include/chunk.h
@@ -0,0 +1,31 @@
+/**
+ * @file include/chunk.h
+ */
+
+
+#ifndef _CHUNK_H_
+#define _CHUNK_H_
+
+
+struct chunk_pool {
+	struct list_head full;
+	struct list_head empty;
+
+	unsigned long align;
+
+	void *(*alloc)(size_t size);
+	void  (*free)(void *addr);
+	size_t (*real_alloc_size)(void *addr);
+};
+
+void *chunk_alloc(struct chunk_pool *pool, size_t size);
+
+void chunk_free(struct chunk_pool *pool, void *addr);
+
+void chunk_pool_init(struct chunk_pool *pool,
+		     unsigned long align,
+		     void *(*alloc)(size_t size),
+		     void  (*free)(void *addr),
+		     size_t (*real_alloc_size)(void *addr));
+
+#endif /* _CHUNK_H_ */
diff --git a/include/kernel/ar.h b/include/kernel/ar.h
index e6bf8b00fa1a0c51b68db14f51dd71d31feadcef..dde2a9d930e22ad04d2026777fb158e6133290bd 100644
--- a/include/kernel/ar.h
+++ b/include/kernel/ar.h
@@ -45,6 +45,7 @@ struct archive {
 
 
 void ar_list_files(struct archive *a);
+void ar_list_symbols(struct archive *a);
 void *ar_find_file(struct archive *a, const char *name);
 void *ar_find_symbol(struct archive *a, const char *name);
 void ar_free(struct archive *a);
diff --git a/include/kernel/kernel.h b/include/kernel/kernel.h
index 2b5892a7e41f8eea478351be4901a21dba42eb8f..101ffa4aee94ec25517fc501c8d9db0efa3b1036 100644
--- a/include/kernel/kernel.h
+++ b/include/kernel/kernel.h
@@ -3,9 +3,9 @@
 
 #include <compiler.h>
 
-
 #define ALIGN_MASK(x, mask)    (((x) + (mask)) & ~(mask))
 #define ALIGN(x, a)            ALIGN_MASK(x, (typeof(x))(a) - 1)
+#define ALIGN_PTR(x, a)        (typeof(x)) ALIGN((unsigned long) x, a)
 
 
 /* this is a bit crude, but must do for now */
@@ -20,4 +20,35 @@
 #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
 
 
+#define offset_of(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/* linux/kernel.h */
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offset_of(type,member) );})
+
+/* Indirect stringification.  Doing two levels allows the parameter to be a
+ * macro itself.  For example, compile with -DFOO=bar, __stringify(FOO)
+ * converts to "bar".
+ */
+__extension__
+#define __stringify_1(x...)     #x
+#define __stringify(x...)       __stringify_1(x)
+
+
+
+/*
+ * Basic Moron Protector (BMP)™
+ */
+#define BMP() do {				\
+	static enum  {DISABLED, ENABLED} bmp;	\
+						\
+	if (bmp)				\
+		BUG();				\
+						\
+	bmp = ENABLED;			\
+} while (0);
+
+
+
 #endif /* _KERNEL_H_ */
diff --git a/include/kernel/kmem.h b/include/kernel/kmem.h
new file mode 100644
index 0000000000000000000000000000000000000000..4332b4035b6d50a3c3a6aa37ed3323279450d760
--- /dev/null
+++ b/include/kernel/kmem.h
@@ -0,0 +1,18 @@
+/**
+ * @file include/kernel/kmem.h
+ */
+
+#ifndef _KERNEL_KMEM_H_
+#define _KERNEL_KMEM_H_
+
+#include <stddef.h>
+
+void *kmalloc(size_t size);
+void *kcalloc(size_t nmemb, size_t size);
+void *krealloc(void *ptr, size_t size);
+
+void kfree(void *ptr);
+
+void *kmem_init(void);
+
+#endif /* _KERNEL_KMEM_H_ */
diff --git a/include/kernel/mm.h b/include/kernel/mm.h
index 54284daf118f4f2e25769724fef355a61e68bd9f..d96c9898fc4849549b6d8f503f2ceb34d29722be 100644
--- a/include/kernel/mm.h
+++ b/include/kernel/mm.h
@@ -40,6 +40,8 @@ unsigned long mm_allocated_blocks(struct mm_pool *mp);
 
 bool mm_addr_in_pool(struct mm_pool *mp, void *addr);
 
+unsigned long mm_block_size(struct mm_pool *mp, const void *addr);
+
 int mm_init(struct mm_pool *mp, void *base,
 	    size_t pool_size, size_t granularity);
 
diff --git a/include/kernel/module.h b/include/kernel/module.h
index c2d7ff629d4cbc8a93b7b0766aea87f00ecb4407..535b82d38806909ff8ea054368e71c234378a336 100644
--- a/include/kernel/module.h
+++ b/include/kernel/module.h
@@ -47,4 +47,6 @@ int apply_relocate_add(struct elf_module *m, Elf_Rela *rel, Elf_Addr sym);
 struct module_section *find_mod_sec(const struct elf_module *m,
 				    const char *name);
 
+int module_load(struct elf_module *m, void *p);
+
 #endif /* _KERNEL_MODULE_H_ */
diff --git a/include/kernel/page.h b/include/kernel/page.h
index 0c1a3a94628934799b04e3fec53029effbd78835..85801cb049eba3773684858b15c2d91a667a0abc 100644
--- a/include/kernel/page.h
+++ b/include/kernel/page.h
@@ -22,4 +22,7 @@ struct page_map_node {
 #define PAGE_MAP_MOVE_NODE_AVAIL_THRESH 1 
 #endif
 
+unsigned long page_map_get_chunk_size(void *addr);
+
+
 #endif /* _KERNEL_PAGE_H_ */
diff --git a/include/kernel/sbrk.h b/include/kernel/sbrk.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4a97bc21513a9dde79cd32f17cd70e9d2816a3b
--- /dev/null
+++ b/include/kernel/sbrk.h
@@ -0,0 +1,12 @@
+/**
+ * @file include/kernel/sbrk.h
+ */
+
+#ifndef _KERNEL_SBRK_H_
+#define _KERNEL_SBRK_H_
+
+#include <stdint.h>
+
+void *kernel_sbrk(intptr_t increment);
+
+#endif /* _KERNEL_SBRK_H_ */
diff --git a/include/kernel/sysctl.h b/include/kernel/sysctl.h
index 3f11d1abf7cf7931fbf7e10913b4bb7e51bf0539..d24ea0792e046a295b92cff096bf7052d99b6042 100644
--- a/include/kernel/sysctl.h
+++ b/include/kernel/sysctl.h
@@ -7,25 +7,11 @@
 
 #include <sys/types.h>
 #include <list.h>
+#include <kernel/kernel.h>
 
 #ifdef offsetof
 #undef offsetof
 #endif
-/* linux/stddef.h */
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-
-/* linux/kernel.h */
-#define container_of(ptr, type, member) ({                      \
-        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
-        (type *)( (char *)__mptr - offsetof(type,member) );})
-
-/* Indirect stringification.  Doing two levels allows the parameter to be a
- * macro itself.  For example, compile with -DFOO=bar, __stringify(FOO)
- * converts to "bar".
- */
-__extension__
-#define __stringify_1(x...)     #x
-#define __stringify(x...)       __stringify_1(x)
 
 /* sysfs.h, modified */
 #define __ATTR(_name, _show, _store) {                          \
diff --git a/init/main.c b/init/main.c
index 6004c42a810378a4d7d26df48dc490a35e1cc91b..eb133e0023c362185ae0ddd96bbde1efdf002bd0 100644
--- a/init/main.c
+++ b/init/main.c
@@ -10,10 +10,17 @@
 
 #include <kernel/ksym.h> /* lookup_symbol */
 
+#include <kernel/kernel.h>
 #include <kernel/printk.h>
+#include <kernel/kmem.h>
+#include <kernel/sbrk.h>
+
+
+void module_image_load_embedded(void);
+void *module_lookup_embedded(char *mod_name);
+void *module_lookup_symbol_embedded(char *sym_name);
 
 
-int module_load(struct elf_module *m, void *p);
 
 static void kernel_init(void)
 {
@@ -25,18 +32,29 @@ static void kernel_init(void)
 
 int main(void)
 {
+	kernel_init();
 
-	struct elf_module m;
+#if 0
+	{
+		struct elf_module m;
+		void *addr;
 
-	kernel_init();
+		printk("%s at %p\n", "printk", lookup_symbol("printk"));
+
+		module_image_load_embedded();
 
-	printk("%s at %p\n", "printk", lookup_symbol("printk"));
-	printk("%s at %p\n", "printf", lookup_symbol("printf"));
+		/*  load -binary kernel/test.ko 0xA0000000 */
+		/* module_load(&m, (char *) 0xA0000000); */
 
 
-	module_load(&m, (char *) 0xA0000000);
-	/*  load -binary kernel/test.ko 0xA0000000 */
+		addr = kmalloc(4096);
+		//addr = module_lookup_embedded("testmodule.ko");
+		addr = module_lookup_symbol_embedded("somefunction");
 
+		if (addr)
+			module_load(&m, addr);
+	}
+#endif
 	return 0;
 }
 
diff --git a/init/modules-image.c b/init/modules-image.c
index 51de991cf7de44f8e86fee9767c50662d66eb97b..ae18f7f00d6304148e5d4b16797047a225dc6320 100644
--- a/init/modules-image.c
+++ b/init/modules-image.c
@@ -2,7 +2,32 @@
  * linker references to embedded modules.image
  */
 
-extern unsigned char _binary_modules_image_start;
-extern unsigned char _binary_modules_image_end;
-extern unsigned char _binary_modules_image_size;
+#include <kernel/printk.h>
+#include <kernel/ar.h>
 
+extern unsigned char _binary_modules_image_start __attribute__((weak));
+extern unsigned char _binary_modules_image_end __attribute__((weak));
+extern unsigned char _binary_modules_image_size __attribute__((weak));
+
+struct archive mod_ar;
+
+
+void module_image_load_embedded(void)
+{
+	ar_load(&_binary_modules_image_start,
+		(unsigned int)&_binary_modules_image_size, &mod_ar);
+
+	ar_list_files(&mod_ar);
+	ar_list_symbols(&mod_ar);
+}
+
+
+void *module_lookup_symbol_embedded(char *sym_name)
+{
+	return ar_find_symbol(&mod_ar, sym_name);
+}
+
+void *module_lookup_embedded(char *mod_name)
+{
+	return ar_find_file(&mod_ar, mod_name);
+}
diff --git a/kernel/Makefile b/kernel/Makefile
index 2bf505fe0d8d401b1bb7d5b688369ff5256e4685..9d67633a7d80f513be3027459ac1e62821b0455c 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -1,5 +1,7 @@
+obj-y += kmem.o
 obj-y += ksym.o
 obj-$(CONFIG_KERNEL_PRINTK) += printk.o
 obj-y += bitmap.o
 obj-y += module.o
 obj-m += testmodule.o
+obj-m += testchain.o
diff --git a/kernel/kmem.c b/kernel/kmem.c
new file mode 100644
index 0000000000000000000000000000000000000000..8f0489cbecda2e1f1574a991f3e316c72256263a
--- /dev/null
+++ b/kernel/kmem.c
@@ -0,0 +1,374 @@
+/**
+ * @file kernel/kmem.c
+ *
+ * A simple high-level allocator that uses sbrk() to retrieve memory.
+ * Is similar to lib/chunk.c, but a lot simpler. I need to respect sbrk()
+ * so I can't just use _chunk_ because it may release any parent chunk,
+ * while we need to do that from _kmem_last  to first as the become free.
+ * I don't have time do do this properly, so this must do for now..
+ */
+
+#include <list.h>
+#include <kernel/kmem.h>
+#include <kernel/sbrk.h>
+#include <kernel/kernel.h>
+#include <kernel/printk.h>
+
+
+#define WORD_ALIGN(x)	ALIGN((x), (sizeof(unsigned long) - 1))
+
+
+struct kmem {
+	void *data;
+	size_t size;
+	int free;
+	struct kmem *prev, *next;
+	struct list_head node;
+} __attribute__((aligned));
+
+/* the initial and the most recent allocation */
+static struct kmem *_kmem_init;
+static struct kmem *_kmem_last;
+
+
+/**
+ * @brief see if we can find a suitable chunk in our pool
+ */
+
+static struct kmem *kmem_find_free_chunk(size_t size, struct kmem **prev)
+{
+	struct kmem *p_tmp;
+	struct kmem *p_elem;
+
+	(*prev) = _kmem_last;
+
+	if (list_empty(&_kmem_init->node))
+		return NULL;
+
+	list_for_each_entry_safe(p_elem, p_tmp, &_kmem_init->node, node) {
+		if (!p_elem->free)
+			BUG();
+
+		if (p_elem->size >= size) {
+			(*prev) = p_elem->prev;
+			return p_elem;
+		}
+	}
+
+
+	return NULL;
+}
+
+
+/**
+ * @brief split a chunk in two for a given size
+ */
+
+static void kmem_split(struct kmem *k, size_t size)
+{
+	struct kmem *split;
+
+
+	split = (struct kmem *)((size_t) k + size);
+
+
+	split->free = 1;
+	split->data = split + 1;
+
+	split->prev = k;
+	split->next = k->next;
+
+	split->size = k->size - size;
+
+	k->size = size - sizeof(struct kmem);
+
+	if (k->next)
+		k->next->prev = split;
+
+	k->next = split;
+
+	list_add_tail(&split->node, &_kmem_init->node);
+}
+
+
+
+/**
+ * @brief returns the initial kmem chunk
+ *
+ * @note call this once kernel_sbrk() works
+ */
+
+void *kmem_init(void)
+{
+	if (likely(_kmem_init))
+		return _kmem_init;
+
+	_kmem_init = kernel_sbrk(WORD_ALIGN(sizeof(struct kmem)));
+
+	if (_kmem_init == (void *) -1) {
+		pr_crit("KMEM: error, cannot _kmem_initialise\n");
+		return NULL;
+	}
+
+	_kmem_init->data = NULL;
+	_kmem_init->size = 0;
+	_kmem_init->free = 0;
+	_kmem_init->prev = NULL;
+	_kmem_init->next = NULL;
+
+	/* we track our free chunks in the node of the initial allocation */
+	INIT_LIST_HEAD(&_kmem_init->node);
+
+	_kmem_last = _kmem_init;
+
+	return _kmem_init;
+}
+
+
+
+/**
+ * @brief merge a chunk with its neighbour
+ */
+
+static void kmem_merge(struct kmem *k)
+{
+	k->size = k->size + k->next->size + sizeof(struct kmem);
+
+	k->next = k->next->next;
+
+	if (k->next)
+		k->next->prev = k;
+}
+
+
+/**
+ * @brief allocates size bytes and returns a pointer to the allocated memory,
+ *	  suitably aligned for any built-in type
+ *
+ * @param size the number of bytes to allocate
+ *
+ * @returns a pointer or NULL on error or size == 0
+ */
+
+void *kmalloc(size_t size)
+{
+	size_t len;
+
+	struct kmem *k_new;
+	struct kmem *k_prev = NULL;
+
+
+	if (!size)
+		return NULL;
+
+	len = WORD_ALIGN(size + sizeof(struct kmem));
+
+	/* try to locate a free chunk first */
+	k_new = kmem_find_free_chunk(size, &k_prev);
+
+	if (k_new) {
+		/* take only what we need */
+		if ((len + sizeof(struct kmem)) < k_new->size)
+			kmem_split(k_new, len);
+
+		k_new->free = 0;
+
+		return k_new->data;
+	}
+
+
+	/* need fresh memory */
+	k_new = kernel_sbrk(len);
+
+	if (k_new == (void *) -1)
+		return NULL;
+
+	k_new->free = 0;
+
+	k_new->next = NULL;
+
+	/* link */
+	k_new->prev = k_prev;
+	k_prev->next = k_new;
+
+	k_new->size = len - sizeof(struct kmem);
+
+	/* data section follows just after */
+	k_new->data = k_new + 1;
+
+	_kmem_last = k_new;
+
+	return k_new->data;
+}
+
+
+/**
+ * @brief allocates memory for an array of nmemb elements of size bytes each and
+ *	   returns a pointer to the allocated memory. The memory is set to zero.
+ *
+ * @param nmemb the number of elements
+ * @param size the number of bytes per element
+ *
+ * @returns a pointer or NULL on error or nmemb/size == 0
+ */
+
+void *kcalloc(size_t nmemb, size_t size)
+{
+	size_t i;
+	size_t len;
+
+	char *dst;
+	void *ptr;
+
+
+	len = nmemb * size;
+
+	ptr = kmalloc(len);
+
+	if (ptr) {
+		dst = ptr;
+		for (i = 0; i < len; i++)
+			dst[i] = 0;
+	}
+
+	return ptr;
+}
+
+
+/**
+ * @brief changes the size of the memory block pointed to by ptr to size bytes.
+ *	  The contents will be unchanged in the range from the start of the
+ *	  region up to the minimum of the old and new sizes. If the new size is
+ *	  larger than the old size,the added memory will not be initialized.
+ *
+ * @param ptr the old memory block, if NULL, this function is equal to kmalloc()
+ * @param size the number of bytes for the new block, if 0, this is equal to
+ *	  kfree()
+ *
+ * @returns a pointer or NULL on error or size == 0
+ */
+
+void *krealloc(void *ptr, size_t size)
+{
+	size_t i;
+
+	size_t len;
+
+	char *dst;
+	char *src;
+
+	void *ptr_new;
+	struct kmem *k;
+
+
+
+	if (!ptr)
+		return kmalloc(size);
+
+	if (ptr < kmem_init()) {
+		pr_warning("KMEM: invalid krealloc() of addr %p below lower "
+			   "bound of trackable memory in call from %p\n",
+			   ptr, __caller(0));
+		return NULL;
+	}
+
+	if (ptr > kernel_sbrk(0)) {
+		pr_warning("KMEM: invalid krealloc() of addr %p beyond system "
+			   "break in call from %p\n",
+			   ptr, __caller(0));
+		return NULL;
+	}
+
+
+	k = ((struct kmem *) ptr) - 1;
+
+	if (k->data != ptr) {
+		pr_warning("KMEM: invalid krealloc() of addr %p in call "
+			   "from %p\n",
+			   ptr, __caller(0));
+		return NULL;
+	}
+
+
+	ptr_new = kmalloc(size);
+
+	if (!ptr_new)
+		return NULL;
+
+
+	if (k->size > size)
+		len = size;
+	else
+		len = k->size;
+
+	src = ptr;
+	dst = ptr_new;
+
+	for (i = 0; i < len; i++)
+		dst[i] = src[i];
+
+	kfree(ptr);
+
+	return ptr_new;
+}
+
+
+/**
+ * @brief function frees the memory space pointed to by ptr, which must have
+ *	  been returned by a previous call to kmalloc(), kcalloc(),
+ *	  or krealloc()
+ *
+ * @param ptr the memory to free
+ */
+
+void kfree(void *ptr)
+{
+	struct kmem *k;
+
+
+	if (!ptr)
+		return;
+
+	if (ptr < kmem_init()) {
+		pr_warning("KMEM: invalid kfree() of addr %p below lower bound "
+			   "of trackable memory in call from %p\n",
+			   ptr, __caller(0));
+		return;
+	}
+
+	if (ptr > kernel_sbrk(0)) {
+		pr_warning("KMEM: invalid kfree() of addr %p beyond system "
+			   "break in call from %p\n",
+			   ptr, __caller(0));
+		return;
+	}
+
+
+	k = ((struct kmem *) ptr) - 1;
+
+	if (k->data != ptr) {
+		pr_warning("KMEM: invalid kfree() of addr %p in call from %p\n",
+			   ptr, __caller(0));
+		return;
+	}
+
+	k->free = 1;
+
+	if (k->next && k->next->free)
+		kmem_merge(k);
+
+	if (k->prev->free) {
+		k = k->prev;
+		kmem_merge(k);
+	}
+
+	if (!k->next) {
+		k->prev->next = NULL;
+		_kmem_last = k->prev;
+
+		/* release back */
+		kernel_sbrk(-(k->size + sizeof(struct kmem)));
+	} else {
+		list_add_tail(&k->node, &_kmem_init->node);
+	}
+}
diff --git a/kernel/module.c b/kernel/module.c
index 46eeebfb11b83ebb9dab103ea604caf359faa118..6915cf59ab7739720c048b825a4a97f6c47e9c92 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -186,7 +186,7 @@ static char *get_strtab_str(const struct elf_module *m, unsigned int idx)
 	return NULL;
 }
 
-	
+
 /**
  * @brief get the name of a symbol in .symtab with a given index
  *
@@ -203,10 +203,10 @@ static char *get_symbol_str(const struct elf_module *m, unsigned int idx)
 
 	//symbols = (Elf_Sym *) (m->pa + symtab->sh_offset);
 	symbols = (Elf_Sym *) ((unsigned long) m->ehdr + symtab->sh_offset);
-	
+
 	if (idx < symtab->sh_size / symtab->sh_entsize)
 		return m->str + symbols[idx].st_name;
-	
+
 	return NULL;
 }
 
@@ -215,11 +215,11 @@ static char *get_symbol_str(const struct elf_module *m, unsigned int idx)
 /**
  * @brief find module section by name
  *
- * @return module section structure pointer or NULL if not found 
+ * @return module section structure pointer or NULL if not found
  */
 
 struct module_section *find_mod_sec(const struct elf_module *m,
-				    const char *name) 
+				    const char *name)
 
 {
 	size_t i;
@@ -306,19 +306,19 @@ void dump_sections(const struct elf_module *m)
 
 
 /**
- * @brief get the type of a symbol 
+ * @brief get the type of a symbol
  *
  * @return 1 if symbol has been found, 0 otherwise
  */
 
 static unsigned long get_symbol_type(const struct elf_module *m,
-				     const char *name) 
+				     const char *name)
 
 {
 	unsigned int i;
 	unsigned int idx;
 	size_t sym_cnt;
-	
+
 	Elf_Shdr *symtab;
 	Elf_Sym *symbols;
 
@@ -326,7 +326,7 @@ static unsigned long get_symbol_type(const struct elf_module *m,
 	idx = find_sec(m, ".symtab");
 
 	if (!idx) {
-		printf("WARN: no .symtab section found\n");
+		printk("WARN: no .symtab section found\n");
 		return -1;
 	}
 
@@ -336,11 +336,11 @@ static unsigned long get_symbol_type(const struct elf_module *m,
 		printk("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
 		return -1;
 	}
-	
+
 	symbols = (Elf_Sym *) ((unsigned long) m->ehdr + symtab->sh_offset);
-	
+
 	sym_cnt = symtab->sh_size / symtab->sh_entsize;
-	
+
 	for (i = 0; i < sym_cnt; i++) {
 		if(!strcmp(get_symbol_str(m, i), name)) {
 			return ELF_ST_TYPE(symbols[i].st_info);
@@ -353,19 +353,19 @@ static unsigned long get_symbol_type(const struct elf_module *m,
 
 
 /**
- * @brief get the value of a symbol 
+ * @brief get the value of a symbol
  *
  * @return 1 if symbol has been found, 0 otherwise
  */
 
 static unsigned long get_symbol_value(const struct elf_module *m,
-				      const char *name, unsigned long *value) 
+				      const char *name, unsigned long *value)
 
 {
 	unsigned int i;
 	unsigned int idx;
 	size_t sym_cnt;
-	
+
 	Elf_Shdr *symtab;
 	Elf_Sym *symbols;
 
@@ -373,21 +373,21 @@ static unsigned long get_symbol_value(const struct elf_module *m,
 	idx = find_sec(m, ".symtab");
 
 	if (!idx) {
-		printf("WARN: no .symtab section found\n");
+		printk("WARN: no .symtab section found\n");
 		return -1;
 	}
 
 	symtab = &m->shdr[idx];
 
 	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
-		printf("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
+		printk("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
 		return -1;
 	}
-	
+
 	symbols = (Elf_Sym *) ((unsigned long) m->ehdr + symtab->sh_offset);
-	
+
 	sym_cnt = symtab->sh_size / symtab->sh_entsize;
-	
+
 	for (i = 0; i < sym_cnt; i++) {
 		if(!strcmp(get_symbol_str(m, i), name)) {
 			(*value) =  symbols[i].st_value;
@@ -416,58 +416,58 @@ static int dump_symtab(struct elf_module *m)
 	idx = find_sec(m, ".symtab");
 
 	if (!idx) {
-		printf("WARN: no .symtab section found\n");
+		printk("WARN: no .symtab section found\n");
 		return -1;
 	}
 
 	symtab = &m->shdr[idx];
 
 	if (symtab->sh_entsize != sizeof(Elf_Sym)) {
-		printf("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
+		printk("Error %d != %ld\n", sizeof(Elf_Sym), symtab->sh_entsize);
 		return -1;
 	}
-	
+
 	symbols = (Elf_Sym *) ((unsigned long) m->ehdr + symtab->sh_offset);
 
 	sym_cnt = symtab->sh_size / symtab->sh_entsize;
 
 
-	printf("\n.symtab contains %d entries\n"
+	printk("\n.symtab contains %d entries\n"
 	       "============================\n"
 	       "\t[NUM]\t[VALUE]\t\t\t[SIZE]\t[TYPE]\t[NAME]\n", sym_cnt);
 
-	
+
 	for (i = 0; i < sym_cnt; i++) {
 
-		printf("\t%d\t%016lx\t%4ld",
+		printk("\t%d\t%016lx\t%4ld",
 		       i,
 		       symbols[i].st_value,
 		       symbols[i].st_size);
 
 		switch (ELF_ST_TYPE(symbols[i].st_info)) {
 		case STT_NOTYPE  :
-			printf("\tNOTYPE "); break;  
+			printk("\tNOTYPE "); break;
 		case STT_OBJECT  :
-			printf("\tOBJECT "); break;
+			printk("\tOBJECT "); break;
 		case STT_FUNC    :
-			printf("\tFUNC   "); break;
+			printk("\tFUNC   "); break;
 		case STT_SECTION :
-			printf("\tSECTION"); break;
+			printk("\tSECTION"); break;
 		case STT_FILE    :
-			printf("\tFILE   "); break;
+			printk("\tFILE   "); break;
 		case STT_COMMON  :
-			printf("\tCOMMON "); break;
+			printk("\tCOMMON "); break;
 		case STT_TLS     :
-			printf("\tTLS    "); break;
+			printk("\tTLS    "); break;
 		default:
-			printf("\tUNKNOWN"); break;
+			printk("\tUNKNOWN"); break;
 		}
 
-		printf("\t%-10s\n", get_symbol_str(m, i));
+		printk("\t%-10s\n", get_symbol_str(m, i));
 
 	}
-	
-	return 0;	
+
+	return 0;
 }
 
 
@@ -487,16 +487,16 @@ static void dump_strtab(const struct elf_module *m)
 		return;
 
 
-	printf("\n.strtab:\n"
+	printk("\n.strtab:\n"
 	       "============================\n"
 	       "\t[OFF]\t[STR]\n");
 
 	while(i < m->sh_size) {
-		printf("\t[%d]\t%s\n", i, m->str + i);
+		printk("\t[%d]\t%s\n", i, m->str + i);
 		i += strlen(m->str + i) + 1;
 	}
 
-	printf("\n\n");
+	printk("\n\n");
 }
 
 
@@ -554,7 +554,7 @@ static int set_dynstr(struct elf_module *m)
 		m->dyn_size = m->shdr[idx].sh_size;
 		return 1;
 	}
-	
+
 	m->dyn_str = NULL;
 	m->dyn_size = 0;
 
@@ -578,7 +578,7 @@ static int set_strtab(struct elf_module *m)
 
 	if (idx) {
 		m->str      = (((char *) m->ehdr) + m->shdr[idx].sh_offset);
-		m->str_size = m->shdr[idx].sh_size; 
+		m->str_size = m->shdr[idx].sh_size;
 		return 1;
 	}
 
@@ -613,17 +613,17 @@ static int setup_module(struct elf_module *m)
 		m->shdr = (Elf_Shdr *) (((char *) m->ehdr) + m->ehdr->e_shoff);
 	} else {
 		m->shdr = NULL;
-		printf("ERR: no section header found\n");
+		printk("ERR: no section header found\n");
 		return -1;
 	}
 
 	/* locate and set section header string table */
 	if (!set_shstrtab(m))
 		return -1;
-	
+
 	/* locate and set dynamic string table */
 	if (!set_dynstr(m)) {
-		printf("WARN: no dynamic string table found\n");
+		printk("WARN: no dynamic string table found\n");
 	}
 
 	/* locate and set string table */
@@ -632,7 +632,7 @@ static int setup_module(struct elf_module *m)
 
 	/* set up for relocatable object */
 	if (m->ehdr->e_type == ET_REL) {
-		printf("TODO\n");
+		printk("TODO\n");
 
 		m->align = 0x200000;	/* PC */
 #if 0
@@ -640,14 +640,14 @@ static int setup_module(struct elf_module *m)
 		m->size = 0;
 		for (i = 0; i < m->ehdr->e_shnum; i++) {
 			if ((m->shdr[i].sh_flags & SHF_ALLOC)) {
-				printf("Alloc section: %s, size %ld\n",
+				printk("Alloc section: %s, size %ld\n",
 				       m->sh_str + m->shdr[i].sh_name,
 				       m->shdr[i].sh_size);
-				
+
 				m->size += m->shdr[i].sh_size;
 
 				if (m->shdr[idx].sh_addralign > m->align)
-		
+
 					m->align = m->shdr[idx].sh_addralign;
 
 			}
@@ -685,7 +685,7 @@ static int module_load_mem(struct elf_module *m)
 	m->pa = (unsigned long) mem;
 
 
-	printf("\n\nLoading module run-time sections\n");
+	printk("\n\nLoading module run-time sections\n");
 
 	va_load = m->va;
 	pa_load = m->pa;
@@ -724,13 +724,13 @@ static int module_load_mem(struct elf_module *m)
 
 
 		if (sec->sh_type & SHT_NOBITS) {
-			printf("\tZero segment %10s at %p size %ld\n",
+			printk("\tZero segment %10s at %p size %ld\n",
 			       s->name, (char *) va_load,
 			       sec->sh_size);
 
 			bzero((void *) va_load, s->size);
 		} else {
-			printf("\tCopy segment %10s from %p to %p size %ld\n",
+			printk("\tCopy segment %10s from %p to %p size %ld\n",
 			       s->name,
 			       (char *) m->ehdr + sec->sh_offset,
 			       (char *) va_load,
@@ -743,11 +743,11 @@ static int module_load_mem(struct elf_module *m)
 
 		s->addr = va_load;
 		va_load = s->addr + s->size;
-		
+
 		s++;
 
 		if (s > &m->sec[m->num_sec]) {
-			printf("Error out of section memory\n");
+			printk("Error out of section memory\n");
 			return -1;
 		}
 	}
@@ -764,7 +764,7 @@ static int module_relocate(struct elf_module *m)
 	size_t rel_cnt;
 
 	Elf_Shdr *sec;
-	
+
 
 
 	/* no dynamic linkage, so it's either self-contained or bugged, we'll
@@ -787,7 +787,7 @@ static int module_relocate(struct elf_module *m)
 		sec = get_sec(m, idx);
 
 		printk("\nSection Header info: %ld\n", sec->sh_info);
-		
+
 		if (sec) {
 
 			Elf_Rela *relatab;
@@ -805,15 +805,15 @@ static int module_relocate(struct elf_module *m)
 				char *symstr = get_symbol_str(m, symsec);
 				struct module_section *s;
 				struct module_section *text  = find_mod_sec(m, ".text");
-				
-				printf("OFF: %08lx INF: %8lx ADD: %3ld LNK: %ld SEC: %d NAME: %s\n\n",
+
+				printk("OFF: %08lx INF: %8lx ADD: %3ld LNK: %ld SEC: %d NAME: %s\n\n",
 				       relatab[i].r_offset,
 				       relatab[i].r_info,
 				       relatab[i].r_addend,
 				       sec->sh_link,
 				       symsec,
 				       symstr);
-			
+
 
 
 				if (strlen(symstr)) {
@@ -822,15 +822,15 @@ static int module_relocate(struct elf_module *m)
 					if (!sym) {
 
 						unsigned long symval;
-						printf("\tNot found in library, resolving in module\n");
+						printk("\tNot found in library, resolving in module\n");
 
 
 						if (!(get_symbol_type(m, symstr) & STT_FUNC)) {
-							printf("\tERROR, unresolved symbol %s\n", symstr);
+							printk("\tERROR, unresolved symbol %s\n", symstr);
 							return -1;
 						}
 						if (!get_symbol_value(m, symstr, &symval)) {
-							printf("\tERROR, unresolved symbol %s\n", symstr);
+							printk("\tERROR, unresolved symbol %s\n", symstr);
 							return -1;
 						}
 
@@ -838,32 +838,32 @@ static int module_relocate(struct elf_module *m)
 
 					}
 
-					printf("\tSymbol %s at %lx\n", symstr, sym);
+					printk("\tSymbol %s at %lx\n", symstr, sym);
 
 					apply_relocate_add(m, &relatab[i], sym);
 
 				} else  { /* no string, symtab entry is probably a section, try to identify it */
-				
+
 					char *secstr = get_shstrtab_str(m, symsec);
 
 					s = find_mod_sec(m, secstr);
 
 					if (!s) {
-						printf("Error cannot locate section %s for symbol\n", secstr);
+						printk("Error cannot locate section %s for symbol\n", secstr);
 							continue;
 					}
 					/* target address to insert at location */
 					reladdr = (long) s->addr;
-					printf("\tRelative symbol address: %x, entry at %08lx\n", reladdr, s->addr);
-					
+					printk("\tRelative symbol address: %x, entry at %08lx\n", reladdr, s->addr);
+
 					apply_relocate_add(m, &relatab[i], reladdr);
 				}
 
-				printf("\n");
+				printk("\n");
 
 
 			}
-			printf("\n");
+			printk("\n");
 		}
 	}
 
@@ -886,7 +886,7 @@ void go(entrypoint_t ep)
 int module_load(struct elf_module *m, void *p)
 {
 	unsigned long symval;
-	entrypoint_t ep; 
+	entrypoint_t ep;
 
 
 	/* the ELF binary starts with the ELF header */
@@ -898,32 +898,30 @@ int module_load(struct elf_module *m, void *p)
 		return -1;
 
 	printk("Setting up module configuration\n");
-	
+
 	if (setup_module(m))
 		return -1;
 
 
 	dump_sections(m);
-	
+
 	if (module_load_mem(m))
 		return -1;
-	
+
 	dump_symtab(m);
 
 	if (module_relocate(m))
 		return -1;
 
 #if 1
-	//uintptr_t epaddr = (uintptr_t) (m->va);
 	if (!get_symbol_value(m, "_module_init", &symval)) {
-		printf("module init not found\n");
+		printk("module init not found\n");
 		return -1;
-
 	}
 
 	ep = (entrypoint_t) (m->va + symval);
-	
-	printf("Binary entrypoint is %lx; invoking %p\n", m->ehdr->e_entry, ep);
+
+	printk("Binary entrypoint is %lx; invoking %p\n", m->ehdr->e_entry, ep);
 
 	go(ep);
 #endif
diff --git a/kernel/module_image.c b/kernel/module_image.c
new file mode 100644
index 0000000000000000000000000000000000000000..51de991cf7de44f8e86fee9767c50662d66eb97b
--- /dev/null
+++ b/kernel/module_image.c
@@ -0,0 +1,8 @@
+/**
+ * linker references to embedded modules.image
+ */
+
+extern unsigned char _binary_modules_image_start;
+extern unsigned char _binary_modules_image_end;
+extern unsigned char _binary_modules_image_size;
+
diff --git a/kernel/testmodule.c b/kernel/testmodule.c
index 362018baf2465d04eb87e9cd7336c7b00310f1ff..f147c1b03740242a5ad481515f9344196d3d41f8 100644
--- a/kernel/testmodule.c
+++ b/kernel/testmodule.c
@@ -6,6 +6,11 @@
 
 #include <kernel/printk.h>
 
+void somefunction(void)
+{
+	printf("this is some function\n");
+}
+
 
 void _module_init(void);
 
diff --git a/lib/Kconfig b/lib/Kconfig
index c504a517ddb3f547f9c11a470c2e95afbdfc8acb..80190205372148d8113dcd75c72a7de920b38d74 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -82,6 +82,16 @@ config PAGE_MAP_MOVE_NODE_AVAIL_THRESH
 	  threshold above. If unsure, use a threshold of 1.
 endmenu
 
+
+config CHUNK
+	bool "Memory Chunk Manager"
+	default y
+	help
+	 Manages smaller than page-sized allocations with memory supplied by
+	 a higher-tier memory-manager. This is a stepping stone to malloc(),
+	 if unsure, say Y
+
+
 config AR
 	bool "AR archive loading support"
 	default y
diff --git a/lib/Makefile b/lib/Makefile
index 4faa3d468b88255cc6f03755a2ae3067a0714a8b..c5c84f0d404b8b95c407b82f28495af0810f52ae 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -2,3 +2,4 @@ lib-$(CONFIG_SYSCTL)	+= sysctl.o
 lib-$(CONFIG_MM)	+= mm.o
 lib-$(CONFIG_PAGE_MAP)	+= page.o
 lib-$(CONFIG_AR)	+= ar.o
+lib-$(CONFIG_CHUNK)	+= chunk.o
diff --git a/lib/ar.c b/lib/ar.c
index 44c5307bb05ae6fa6ef3c1debd8ff8349778975d..842c09d3c1d71229e094a160c5cbd774929fcfc9 100644
--- a/lib/ar.c
+++ b/lib/ar.c
@@ -395,14 +395,51 @@ static unsigned int ar_get_filecount(char *p, struct archive *a)
 
 
 /**
- * @brief print files in the archive
+ * @brief print symbols in the archive
  *
  * @param a a struct archive
  * @param name the file name to search for
  *
  * @return a pointer or NULL if not found
+ */
+
+void ar_list_symbols(struct archive *a)
+{
+	unsigned long i;
+
+
+	if (!a)
+		return;
+
+	if (!a->fname)
+		return;
+
+	if (!a->fnamesz)
+		return;
+
+	printk("AR:\n"
+	       "AR: Symbol contents of archive at %p\n"
+	       "AR:\n",
+	       a->ar_base);
+
+	printk("AR:\t[NAME]\t\t\t[ADDR]\n");
+
+	for (i = 0; i < a->n_sym; i++) {
+		printk("AR:\t%s\t\t%p\n",
+		       a->sym[i], a->p_sym[i]);
+	}
+
+	printk("AR:\n");
+}
+
+
+/**
+ * @brief print files in the archive
  *
- * @note this silently returns the first occurence only
+ * @param a a struct archive
+ * @param name the file name to search for
+ *
+ * @return a pointer or NULL if not found
  */
 
 void ar_list_files(struct archive *a)
@@ -423,6 +460,7 @@ void ar_list_files(struct archive *a)
 	       "AR: File contents of archive at %p\n"
 	       "AR:\n",
 	       a->ar_base);
+
 	printk("AR:\t[NAME]\t\t\t[SIZE]\t[ADDR]\n");
 
 	for (i = 0; i < a->n_file; i++) {
diff --git a/lib/chunk.c b/lib/chunk.c
new file mode 100644
index 0000000000000000000000000000000000000000..d23f883f0ef52c544211baa570d1b507bacd00d2
--- /dev/null
+++ b/lib/chunk.c
@@ -0,0 +1,405 @@
+/**
+ * @file lib/chunk.c
+ *
+ *
+ * This is a stepping stone to something like malloc()
+ *
+ * When chunk_alloc() is called, it attempts to first-fit-locate a buffer within
+ * its tracked allocated chunks, splitting them into smaller chunks that track
+ * their parent chunks. If it does not find a sufficiently large chunk, it
+ * uses the user-supplied function to grab memory as needed from a higher-tier
+ * memory manager. In case such a memory manager cannot provide buffers
+ * exactly the requested size, an optional function real_alloc_size() may be
+ * provided, so the chunk allocator can know the actual size of the allocated
+ * block (e.g. from kernel/page.c)
+ *
+ * When freeing an allocation with chunk_free() and the chunk is the child of
+ * another chunk, the reference counter of the parent is decremented and the
+ * child is released and upmerged IF the child was the last allocation in the
+ * parent's buffer, otherwise it is treated as any other available chunk.
+ * If a top-level chunk's refernce counter is decremented to 0, it is released
+ * back to the higher-tier memory manager.
+ *
+ * Note that the address verification in chunk_free() is weak, as it only
+ * checks if it's start address and size are within the chunk of the parent.
+ *
+ * @todo add a function chunk_alloc_aligned() that allows aligned allocations 
+ *	 without wasting space. the function just grabs some memory and creates
+ *	 a single chunk up to the alignement boundary and adds it to the "full"
+ *	 pool, then returns the following (aligned) chunk. 
+ *
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <list.h>
+#include <kernel/kernel.h>
+#include <kernel/printk.h>
+
+#include <chunk.h>
+
+
+/**
+ * The header of an alloccated memory chunk.
+ *
+ * this isn't partcicularly memory-efficient for small allocations...
+ */
+
+struct chunk {
+	void	*mem;		/* the start of the data memory */
+
+	size_t	size;		/* the allocated size of the chunk */
+	size_t	free;		/* the free memory in the chunk */
+
+	unsigned long refcnt;	/* number of references to this chunk */
+	struct chunk *parent;	/* the parent chunk */
+	struct chunk *child;	/* the youngest child chunk */
+	struct chunk *sibling;	/* the left-hand (younger) child chunk */
+
+	struct list_head node;
+};
+
+
+
+
+/**
+ * @brief align a pointer to the alignment configured
+ *
+ * @param pool a struct chunk_pool
+ *
+ * @param p an address pointer
+ *
+ * @return the aligned pointer
+ */
+
+static inline void *chunk_align(struct chunk_pool *pool, void *p)
+{
+	return (void *) (((unsigned long) p + pool->align) & ~pool->align);
+}
+
+
+/**
+ * @brief classify a chunk as full or empty
+ *
+ * @param pool a struct chunk_pool
+ * @param c a struct chunk
+ */
+
+static void chunk_classify(struct chunk_pool *pool, struct chunk *c)
+{
+	/* if it can't fit at least one header, it's no good */
+	if (c->free <= (sizeof(struct chunk) + pool->align))
+		list_move_tail(&c->node, &pool->empty);
+	else
+		list_move_tail(&c->node, &pool->full);
+}
+
+
+/**
+ * @brief configure data pointer and free bytes of a chunk
+ *
+ * @param pool a struct chunk_pool
+ * @param c a struct chunk
+ */
+
+static void chunk_setup(struct chunk_pool *pool, struct chunk *c)
+{
+	/* the actual memory starts after struct chunk */
+	c->mem = chunk_align(pool, (void *) (c + 1));
+	/* set the allocatable size of the cunk */
+	c->free = ((size_t) c + c->size) - (size_t) c->mem;
+
+	chunk_classify(pool, c);
+}
+
+
+/**
+ * @brief grab a new chunk of memory from the higher-tier memory manager
+ *
+ * @param pool a struct chunk_pool
+ *
+ * @param size the requested minimum size of the chunk
+ *
+ * @return a struct chunk or NULL on error
+ */
+
+static struct chunk *chunk_grab_new(struct chunk_pool *pool, size_t size)
+{
+	struct chunk *c;
+
+
+	c = (struct chunk *) pool->alloc(size);
+
+	if (!c)
+		return NULL;
+
+
+	/* if set, get actual chunk size */
+	if (pool->real_alloc_size) {
+
+		c->size = pool->real_alloc_size((void *) c);
+
+		if (c->size < size) {
+			pr_warn("CHUNK: got less bytes than expected from "
+				"higher-tier memory manager\n");
+			pool->free((void *) c);
+			return NULL;
+		}
+
+	} else {
+		c->size = size;
+	}
+
+	/* this is a new toplevel chunk, it's all alone */
+	c->parent  = NULL;
+	c->child   = NULL;
+	c->sibling = NULL;
+
+	/* we have no references yet */
+	c->refcnt = 0;
+
+	/* add new parent to full list by default */
+	list_add_tail(&c->node, &pool->full);
+
+	chunk_setup(pool, c);
+
+
+	return c;
+}
+
+
+/**
+ * @brief split of a new chunk as a child of an existing chunk
+ *
+ * @param pool a struct chunk_pool
+ *
+ * @param c a struct chunk that is the parent and can hold the child
+ *
+ * @param size the size of the new chunk
+ *
+ * @return a struct chunk or NULL on error
+ */
+
+static struct chunk *chunk_split(struct chunk_pool *pool,
+				 struct chunk *c, size_t size)
+{
+	struct chunk *new;
+
+
+	if (c->free < size)
+		return NULL;
+
+	/* this chunk is now a child of a higher-order chunk */
+	new = (struct chunk *) c->mem;
+	new->parent  = c;
+	new->child   = NULL;
+	new->sibling = c->child;
+	new->refcnt  = 1;
+	new->free    = 0;
+	new->size    = size;
+
+	/* the new node will be in use, add to empty list */
+	list_add_tail(&new->node, &pool->empty);
+
+	chunk_setup(pool, new);
+
+	/* track the youngest child in the parent */
+	c->child = new;
+	c->refcnt++;
+
+	/* align parent chunk to start of new memory subsegment */
+	c->mem = chunk_align(pool, (void *) ((size_t) c->mem + size));
+
+	/* update free bytes with regard to actual alignment */
+	c->free = ((size_t) c + c->size) - ((size_t) new  + new->size);
+
+	return new;
+}
+
+
+/**
+ * @brief allocate a chunk of memory
+ *
+ * @param pool a struct chunk_pool
+ * @param size the number of bytes to allocate
+ *
+ * @return a pointer to a memory buffer or NULL on error
+ */
+
+void *chunk_alloc(struct chunk_pool *pool, size_t size)
+{
+	size_t alloc_sz;
+
+	struct chunk *c = NULL;
+
+	struct chunk *p_elem;
+	struct chunk *p_tmp;
+
+
+
+	if (!pool->alloc) {
+		pr_err("CHUNK: error, no allocator supplied.\n");
+		return NULL;
+	}
+
+
+	if (!size)
+		return NULL;
+
+
+	/* grab a large enough chunk to satisfy alignment and overhead needs */
+	alloc_sz = (size_t) chunk_align(pool,
+					(void *) (size + sizeof(struct chunk)));
+
+	list_for_each_entry_safe(p_elem, p_tmp, &pool->full, node) {
+
+		if (p_elem->free >= alloc_sz) {
+			c = p_elem;
+			break;
+		}
+	}
+
+	/* no chunks, either list is empty or none had enough space */
+	if (!c) {
+
+		c = chunk_grab_new(pool, alloc_sz);
+
+		if (!c) {
+			pr_err("CHUNK: error, got no memory from allocator \n");
+			return NULL;
+		}
+	}
+
+	c = chunk_split(pool, c, alloc_sz);
+
+	return c->mem;
+}
+
+
+/**
+ * @brief free a chunk of memory
+ *
+ * @param pool a struct chunk_pool
+ * @param addr the address of the buffer to return to the allocator
+ *
+ */
+
+void chunk_free(struct chunk_pool *pool, void *addr)
+{
+	unsigned long chunk_addr;
+
+	struct chunk *c;
+	struct chunk *p;
+
+
+	if (!addr)
+		return;
+
+
+	if (!pool->alloc) {
+		pr_err("CHUNK: error, no de-allocator supplied.\n");
+		return;
+	}
+
+
+	/* the start of the chunk is off by at most the size of the header minus
+	 * the (alignment - 1)
+	 */
+	chunk_addr = (unsigned long) addr - sizeof(struct chunk) - pool->align;
+	c = (struct chunk *) chunk_align(pool, (void *) chunk_addr);
+
+
+	/* if the address of the data chunk does not coincide with what we just
+	 * calculated, something is seriously wrong here
+	 */
+	BUG_ON((size_t) addr != (size_t) chunk_align(pool, (void *) (c + 1)));
+
+
+	/* if this is a toplevel chunk, release it back to the higher-tier */
+	if (!c->parent) {
+
+		BUG_ON(c->refcnt);
+
+		pool->free((void *) c);
+		list_del(&c->node);
+
+		return;
+	}
+
+
+	p = c->parent;
+
+	/* weak check if this is even valid */
+	if (addr < (void *) p) {
+		pr_warn("CHUNK: invalid address %p supplied in call to %s\n",
+			addr, __func__);
+		return;
+	}
+
+	if (((size_t) c + c->size) > ((size_t) p + p->size)) {
+		pr_warn("CHUNK: invalid address %p, or attempted double free "
+			"in call to %s\n", addr, __func__);
+	}
+
+	/* If this he youngest child, merge it back and update the parent. */
+	if (p->child == c) {
+
+		p->child = c->sibling;
+		p->refcnt--;
+
+		/* align parent chunk */
+		p->mem = chunk_align(pool, (void *) ((size_t) c));
+
+		/* update free parent bytes with regard to actual alignment */
+		p->free = ((size_t) p + p->size) - (size_t) c;
+
+		/* make sure this will not fit the parent without overflowing
+		 * (except for the obvious edge case that should never happen
+		 * anyways), so we may detect a double-free
+		 */
+		c->size = p->size - c->size + 1;
+
+		list_del(&c->node);
+
+		if (!p->refcnt)
+			chunk_free(pool, p->mem);
+
+	} else {
+		chunk_setup(pool, c);
+		list_move_tail(&c->node, &pool->full);
+	}
+}
+
+
+/**
+ * @brief initialise a chunk pool
+ *
+ * @param pool a struct chunk pool
+ * @param align the memory alignment of returned memory pointers
+ * @param alloc a pointer to a higher-tier function that
+ *	  gives us a chunk of memory to manage
+ * @param free a pointer to a function that returns a chunk of memory to the
+ *	  higher-tier memory manager
+ * @param real_alloc_size a pointer to a function that tells us how large the
+ *	  chunk returned by alloc() actually is. This may be NULL if alloc()
+ *	  always returns a chunk the exact size we requested
+ *
+ * @note the alloc() function should at least return word-aligned addresses
+ */
+
+void chunk_pool_init(struct chunk_pool *pool,
+		     unsigned long align,
+		     void *(*alloc)(size_t size),
+		     void  (*free)(void *addr),
+		     size_t (*real_alloc_size)(void *addr))
+{
+	INIT_LIST_HEAD(&pool->full);
+	INIT_LIST_HEAD(&pool->empty);
+
+	pool->align = align - 1;
+
+	pool->alloc = alloc;
+	pool->free  = free;
+
+	pool->real_alloc_size = real_alloc_size;
+}
diff --git a/lib/mm.c b/lib/mm.c
index 126f8079c0b22859bb4f75a286d68a90fe4da1a8..18c8f9653bf07a3fbe868ffabea67e2de8f570d3 100644
--- a/lib/mm.c
+++ b/lib/mm.c
@@ -77,7 +77,7 @@ static bool mm_blk_addr_valid(struct mm_pool *mp, struct mm_blk_lnk *blk)
  * @param mp a struct mm_pool
  * @param blk a struct mm_blk_lnk
  *
- * @return block index or -EFAULT on error
+ * @return block index
  */
 
 static unsigned long mm_blk_idx(struct mm_pool *mp, struct mm_blk_lnk *blk)
@@ -206,6 +206,7 @@ static void *mm_find_neighbour(struct mm_pool *mp,
 	addr ^= (1UL << order);
 	addr += mp->base;
 
+
 	return (void *) addr;
 }
 
@@ -236,6 +237,15 @@ static struct mm_blk_lnk *mm_merge_blk(struct mm_pool *mp,
 	 * is the start of the newly created higher order block
 	 */
 
+	/* There is a potential bug, we should never get this far if the block
+	 * was not allocated, even if the block address in free() was incorrect
+	 */
+	if (!n->link.prev || !n->link.next) {
+		pr_crit("MM: corruption warning, someone tried to release an "
+			"invalid block.\n");
+		return NULL;
+	}
+
 	list_del(&n->link);
 
 	if (n < blk) {
@@ -368,10 +378,15 @@ void *mm_alloc(struct mm_pool *mp, size_t size)
 	unsigned long i;
 	unsigned long order;
 
-	struct mm_blk_lnk *blk  = NULL;
+	struct mm_blk_lnk *blk = NULL;
 	struct list_head *list = NULL;
 
 
+	if (!mp)
+		return NULL;
+
+	if (!size)
+		return NULL;
 
 	order = ilog2(roundup_pow_of_two(size));
 
@@ -480,6 +495,34 @@ exit:
 }
 
 
+
+/**
+ * @brief returns the size of the block for a given address
+ *
+ * @param mp	a struct mm_pool
+ *
+ * @param addr  the address of the block
+ *
+ * @return the size of the block the address is in, 0 if invalid or not found
+ *
+ */
+
+unsigned long mm_block_size(struct mm_pool *mp, const void *addr)
+{
+	unsigned long order;
+
+	unsigned long size = 0;
+
+
+	if (mm_addr_in_pool(mp, (struct mm_blk_link *) addr)) {
+		order = mm_blk_get_alloc_order(mp, (struct mm_blk_lnk *) addr);
+		size = 1 << order;
+	}
+
+	return size;
+}
+
+
 /**
  * @brief returns number of free blocks at block granularity
  *
@@ -662,7 +705,7 @@ void mm_exit(struct mm_pool *mp) {
 
 void mm_dump_stats(struct mm_pool *mp)
 {
-	unsigned long i;
+	unsigned long i __attribute__((unused));
 
 
 	if (!mp)
diff --git a/lib/page.c b/lib/page.c
index e293617f85753e2064452e54f473f8285876bb71..8911e03f038afbf1f1d14825219427dfe0e292c7 100644
--- a/lib/page.c
+++ b/lib/page.c
@@ -27,8 +27,8 @@
 static struct page_map_node **page_mem;
 
 /* empty/busy pool lists */
-struct list_head page_map_list_full;
-struct list_head page_map_list_empty;
+static struct list_head page_map_list_full;
+static struct list_head page_map_list_empty;
 
 
 /**
@@ -80,6 +80,12 @@ int page_map_add(unsigned long start, unsigned long end,
 	if (end < start)
 		goto error;
 
+	if ((end - start) < page_size)
+		goto error;
+
+	if (!page_size)
+		goto error;
+
 
 	mem_size = (size_t) end - start;
 
@@ -176,6 +182,8 @@ error:
  *	 to consider re-adding the free segment before your boot memory back
  *	 to the page map. In that case, make sure the allocation is never
  *	 released. Make sure you configure extra ram banks if needed.
+ *
+ * @note the reserved block is at least size bytes
  */
 
 void *page_map_reserve_chunk(size_t size)
@@ -204,6 +212,56 @@ exit:
 }
 
 
+/**
+ * @brief get the size of the chunk for an address
+ *
+ * @param addr the (page) address pointer
+ *
+ * @return the size of the chunk, or 0 on error or if not found in pool
+ */
+
+unsigned long page_map_get_chunk_size(void *addr)
+{
+	unsigned long size = 0;
+
+	struct page_map_node *p_elem;
+	struct page_map_node *p_tmp;
+
+
+	if (!page_mem) {
+		pr_err("PAGE MEM: %s no page map configured\n", __func__);
+		goto exit;
+	}
+
+	if (!addr) {
+		pr_info("PAGE MEM: NULL pointer in call to %s from %p\n",
+			__func__, __caller(0));
+		goto exit;
+	}
+
+	list_for_each_entry_safe(p_elem, p_tmp, &page_map_list_empty, node) {
+		if (mm_addr_in_pool(p_elem->pool, addr)) {
+			size = mm_block_size(p_elem->pool, addr);
+			goto exit;
+		}
+	}
+
+	list_for_each_entry_safe(p_elem, p_tmp, &page_map_list_full, node) {
+		if (mm_addr_in_pool(p_elem->pool, addr)) {
+			size = mm_block_size(p_elem->pool, addr);
+			goto exit;
+		}
+	}
+
+exit:
+	return size;
+}
+
+
+
+
+
+
 /**
  * @brief allocates a page by trying all configured banks until one is found
  *
@@ -224,6 +282,7 @@ void *page_alloc(void)
 	}
 
 	list_for_each_entry_safe(p_elem, p_tmp, &page_map_list_full, node) {
+
 		page = mm_alloc(p_elem->pool, PG_SIZE(p_elem));
 
 		if (!page) {
diff --git a/samples/Kconfig b/samples/Kconfig
index 47bdb96a373530af681b88cfa6307653e7b93ff5..91214c8d29015e0e8fcb39ea21ccb7b22ed4bc03 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -17,4 +17,10 @@ config SAMPLE_MM
 	help
 	  Build a sample demonstrating the use of the memory management system.
 
+config SAMPLE_CHUNK
+	bool "Build chunk memory allocator sample code"
+	depends on CHUNK
+	help
+	  Build a sample demonstrating the use of the chunk memory allocator.
+
 endif # SAMPLES
diff --git a/samples/Makefile b/samples/Makefile
index 951ae4999c0c6db30f4aad7dae2a38a2e9cd1ceb..541459d1acc37daa45a3f7b8da7e13a16b5c6185 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -2,3 +2,4 @@
 
 obj-$(CONFIG_SAMPLE_SYSCTL)	+= sysctl/
 obj-$(CONFIG_SAMPLE_MM)		+= mm/
+obj-$(CONFIG_SAMPLE_CHUNK)	+= chunk/
diff --git a/samples/chunk/Makefile b/samples/chunk/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b2341f4cd46ce96210c6ac349cc3f1291b283183
--- /dev/null
+++ b/samples/chunk/Makefile
@@ -0,0 +1,28 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-$(CONFIG_SAMPLE_CHUNK) := chunk_demo
+
+# I guess I'm too stupid to figure out the proper way to do this
+# (but maybe there is none)
+
+ifdef CROSS_COMPILE
+HOSTCC := $(CROSS_COMPILE)gcc
+HOSTLD := $(CROSS_COMPILE)ld
+endif
+
+
+HOSTCFLAGS_chunk_demo.o += -I$(objtree)/include
+chunk_demo-objs := chunk_demo.o
+
+ifndef CROSS_COMPILE
+EXTRAPFLAG = -m32
+else
+EXTRAPFLAG =
+endif
+
+HOSTCFLAGS_chunk_demo.o +=  $(EXTRAFLAG)
+HOSTLOADLIBES_chunk_demo += $(EXTRAFLAG) $(objtree)/lib/lib.a
+HOSTLOADLIBES_chunk_demo += $(objtree)/kernel/built-in.o
+
+always := $(hostprogs-y)
diff --git a/samples/chunk/chunk_demo.c b/samples/chunk/chunk_demo.c
new file mode 100644
index 0000000000000000000000000000000000000000..40561e0306adc4bd03e0a8c09b1963ec4b119ca0
--- /dev/null
+++ b/samples/chunk/chunk_demo.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+
+#include <chunk.h>
+#include <stdlib.h>
+
+
+
+/* configure a dummy higher-tier memory-manager */
+
+#define PAGE_SIZE 4096
+
+static void *page_alloc(size_t size)
+{
+	return malloc(PAGE_SIZE);
+}
+
+static void page_free(void *addr)
+{
+	free(addr);
+}
+
+static size_t get_alloc_size()
+{
+	return PAGE_SIZE;
+}
+
+
+#define DO_ALLOC	256
+#define ALIGN_BYTES	8
+
+int main(void)
+{
+	int i;
+
+	struct chunk_pool pool;
+
+	void *p[DO_ALLOC];
+
+
+	chunk_pool_init(&pool, ALIGN_BYTES,
+			&page_alloc, &page_free, &get_alloc_size);
+
+	for (i = 0; i < DO_ALLOC; i++)
+		p[i] = chunk_alloc(&pool, (i + 1) * 17);
+
+	for (i = DO_ALLOC - 1; i >= 0; i--)
+		chunk_free(&pool, p[i]);
+}