Index: linux-2.6.22/arch/sparc64/kernel/pci.c
===================================================================
--- linux-2.6.22.orig/arch/sparc64/kernel/pci.c
+++ linux-2.6.22/arch/sparc64/kernel/pci.c
@@ -422,10 +422,15 @@ struct pci_dev *of_create_pci_dev(struct
 	dev->multifunction = 0;		/* maybe a lie? */
 
 	if (host_controller) {
-		dev->vendor = 0x108e;
-		dev->device = 0x8000;
-		dev->subsystem_vendor = 0x0000;
-		dev->subsystem_device = 0x0000;
+		if (tlb_type != hypervisor) {
+			pci_read_config_word(dev, PCI_VENDOR_ID,
+					     &dev->vendor);
+			pci_read_config_word(dev, PCI_DEVICE_ID,
+					     &dev->device);
+		} else {
+			dev->vendor = PCI_VENDOR_ID_SUN;
+			dev->device = 0x80f0;
+		}
 		dev->cfg_size = 256;
 		dev->class = PCI_CLASS_BRIDGE_HOST << 8;
 		sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
@@ -817,7 +822,7 @@ int pci_host_bridge_read_pci_cfg(struct 
 {
 	static u8 fake_pci_config[] = {
 		0x8e, 0x10, /* Vendor: 0x108e (Sun) */
-		0x00, 0x80, /* Device: 0x8000 (PBM) */
+		0xf0, 0x80, /* Device: 0x80f0 (Fire) */
 		0x46, 0x01, /* Command: 0x0146 (SERR, PARITY, MASTER, MEM) */
 		0xa0, 0x22, /* Status: 0x02a0 (DEVSEL_MED, FB2B, 66MHZ) */
 		0x00, 0x00, 0x00, 0x06, /* Class: 0x06000000 host bridge */
Index: linux-2.6.22/arch/sparc64/kernel/pci_common.c
===================================================================
--- linux-2.6.22.orig/arch/sparc64/kernel/pci_common.c
+++ linux-2.6.22/arch/sparc64/kernel/pci_common.c
@@ -44,6 +44,67 @@ static void *sun4u_config_mkaddr(struct 
 	return (void *)	(pbm->config_space | bus | devfn | reg);
 }
 
+/* At least on Sabre, it is necessary to access all PCI host controller
+ * registers at their natural size, otherwise zeros are returned.
+ * Strange but true, and I see no language in the UltraSPARC-IIi
+ * programmer's manual that mentions this even indirectly.
+ */
+static int sun4u_read_pci_cfg_host(struct pci_pbm_info *pbm,
+				   unsigned char bus, unsigned int devfn,
+				   int where, int size, u32 *value)
+{
+	u32 tmp32, *addr;
+	u16 tmp16;
+	u8 tmp8;
+
+	addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
+	if (!addr)
+		return PCIBIOS_SUCCESSFUL;
+
+	switch (size) {
+	case 1:
+		if (where < 8) {
+			unsigned long align = (unsigned long) addr;
+
+			align &= ~1;
+			pci_config_read16((u16 *)align, &tmp16);
+			if (where & 1)
+				*value = tmp16 >> 8;
+			else
+				*value = tmp16 & 0xff;
+		} else {
+			pci_config_read8((u8 *)addr, &tmp8);
+			*value = (u32) tmp8;
+		}
+		break;
+
+	case 2:
+		if (where < 8) {
+			pci_config_read16((u16 *)addr, &tmp16);
+			*value = (u32) tmp16;
+		} else {
+			pci_config_read8((u8 *)addr, &tmp8);
+			*value = (u32) tmp8;
+			pci_config_read8(((u8 *)addr) + 1, &tmp8);
+			*value |= ((u32) tmp8) << 8;
+		}
+		break;
+
+	case 4:
+		tmp32 = 0xffffffff;
+		sun4u_read_pci_cfg_host(pbm, bus, devfn,
+					where, 2, &tmp32);
+		*value = tmp32;
+
+		tmp32 = 0xffffffff;
+		sun4u_read_pci_cfg_host(pbm, bus, devfn,
+					where + 2, 2, &tmp32);
+		*value |= tmp32 << 16;
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
 static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
 			      int where, int size, u32 *value)
 {
@@ -53,10 +114,6 @@ static int sun4u_read_pci_cfg(struct pci
 	u16 tmp16;
 	u8 tmp8;
 
-	if (bus_dev == pbm->pci_bus && devfn == 0x00)
-		return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where,
-						    size, value);
-
 	switch (size) {
 	case 1:
 		*value = 0xff;
@@ -69,6 +126,10 @@ static int sun4u_read_pci_cfg(struct pci
 		break;
 	}
 
+	if (!bus_dev->number && !PCI_SLOT(devfn))
+		return sun4u_read_pci_cfg_host(pbm, bus, devfn, where,
+					       size, value);
+
 	addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
 	if (!addr)
 		return PCIBIOS_SUCCESSFUL;
@@ -101,6 +162,53 @@ static int sun4u_read_pci_cfg(struct pci
 	return PCIBIOS_SUCCESSFUL;
 }
 
+static int sun4u_write_pci_cfg_host(struct pci_pbm_info *pbm,
+				    unsigned char bus, unsigned int devfn,
+				    int where, int size, u32 value)
+{
+	u32 *addr;
+
+	addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
+	if (!addr)
+		return PCIBIOS_SUCCESSFUL;
+
+	switch (size) {
+	case 1:
+		if (where < 8) {
+			unsigned long align = (unsigned long) addr;
+			u16 tmp16;
+
+			align &= ~1;
+			pci_config_read16((u16 *)align, &tmp16);
+			if (where & 1) {
+				tmp16 &= 0x00ff;
+				tmp16 |= value << 8;
+			} else {
+				tmp16 &= 0xff00;
+				tmp16 |= value;
+			}
+			pci_config_write16((u16 *)align, tmp16);
+		} else
+			pci_config_write8((u8 *)addr, value);
+		break;
+	case 2:
+		if (where < 8) {
+			pci_config_write16((u16 *)addr, value);
+		} else {
+			pci_config_write8((u8 *)addr, value & 0xff);
+			pci_config_write8(((u8 *)addr) + 1, value >> 8);
+		}
+		break;
+	case 4:
+		sun4u_write_pci_cfg_host(pbm, bus, devfn,
+					 where, 2, value & 0xffff);
+		sun4u_write_pci_cfg_host(pbm, bus, devfn,
+					 where + 2, 2, value >> 16);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
 static int sun4u_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
 			       int where, int size, u32 value)
 {
@@ -108,9 +216,10 @@ static int sun4u_write_pci_cfg(struct pc
 	unsigned char bus = bus_dev->number;
 	u32 *addr;
 
-	if (bus_dev == pbm->pci_bus && devfn == 0x00)
-		return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where,
-						     size, value);
+	if (!bus_dev->number && !PCI_SLOT(devfn))
+		return sun4u_write_pci_cfg_host(pbm, bus, devfn, where,
+						size, value);
+
 	addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
 	if (!addr)
 		return PCIBIOS_SUCCESSFUL;
Index: linux-2.6.22/drivers/acpi/resources/rsxface.c
===================================================================
--- linux-2.6.22.orig/drivers/acpi/resources/rsxface.c
+++ linux-2.6.22/drivers/acpi/resources/rsxface.c
@@ -474,8 +474,6 @@ acpi_rs_match_vendor_resource(struct acp
 	return (AE_CTRL_TERMINATE);
 }
 
-ACPI_EXPORT_SYMBOL(acpi_rs_match_vendor_resource)
-
 /*******************************************************************************
  *
  * FUNCTION:    acpi_walk_resources
Index: linux-2.6.22/kernel/kprobes.c
===================================================================
--- linux-2.6.22.orig/kernel/kprobes.c
+++ linux-2.6.22/kernel/kprobes.c
@@ -1054,6 +1054,11 @@ EXPORT_SYMBOL_GPL(register_kprobe);
 EXPORT_SYMBOL_GPL(unregister_kprobe);
 EXPORT_SYMBOL_GPL(register_jprobe);
 EXPORT_SYMBOL_GPL(unregister_jprobe);
+#ifdef CONFIG_KPROBES
 EXPORT_SYMBOL_GPL(jprobe_return);
+#endif
+
+#ifdef CONFIG_KPROBES
 EXPORT_SYMBOL_GPL(register_kretprobe);
 EXPORT_SYMBOL_GPL(unregister_kretprobe);
+#endif
Index: linux-2.6.22/kernel/profile.c
===================================================================
--- linux-2.6.22.orig/kernel/profile.c
+++ linux-2.6.22/kernel/profile.c
@@ -199,11 +199,11 @@ EXPORT_SYMBOL_GPL(register_timer_hook);
 EXPORT_SYMBOL_GPL(unregister_timer_hook);
 EXPORT_SYMBOL_GPL(task_handoff_register);
 EXPORT_SYMBOL_GPL(task_handoff_unregister);
+EXPORT_SYMBOL_GPL(profile_event_register);
+EXPORT_SYMBOL_GPL(profile_event_unregister);
 
 #endif /* CONFIG_PROFILING */
 
-EXPORT_SYMBOL_GPL(profile_event_register);
-EXPORT_SYMBOL_GPL(profile_event_unregister);
 
 #ifdef CONFIG_SMP
 /*
Index: linux-2.6.22/net/mac80211/ieee80211_sta.c
===================================================================
--- linux-2.6.22.orig/net/mac80211/ieee80211_sta.c
+++ linux-2.6.22/net/mac80211/ieee80211_sta.c
@@ -25,7 +25,6 @@
 #include <linux/wireless.h>
 #include <linux/random.h>
 #include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
 #include <net/iw_handler.h>
 #include <asm/types.h>
 
@@ -2073,12 +2072,9 @@ static int ieee80211_sta_config_auth(str
 	struct ieee80211_sta_bss *bss, *selected = NULL;
 	int top_rssi = 0, freq;
 
-	rtnl_lock();
-
 	if (!ifsta->auto_channel_sel && !ifsta->auto_bssid_sel &&
 	    !ifsta->auto_ssid_sel) {
 		ifsta->state = IEEE80211_AUTHENTICATE;
-		rtnl_unlock();
 		ieee80211_sta_reset_auth(dev, ifsta);
 		return 0;
 	}
@@ -2121,7 +2117,6 @@ static int ieee80211_sta_config_auth(str
 		ieee80211_sta_set_bssid(dev, selected->bssid);
 		ieee80211_rx_bss_put(dev, selected);
 		ifsta->state = IEEE80211_AUTHENTICATE;
-		rtnl_unlock();
 		ieee80211_sta_reset_auth(dev, ifsta);
 		return 0;
 	} else {
@@ -2132,7 +2127,6 @@ static int ieee80211_sta_config_auth(str
 		} else
 			ifsta->state = IEEE80211_DISABLED;
 	}
-	rtnl_unlock();
 	return -1;
 }
 
Index: linux-2.6.22/drivers/ata/pata_hpt37x.c
===================================================================
--- linux-2.6.22.orig/drivers/ata/pata_hpt37x.c
+++ linux-2.6.22/drivers/ata/pata_hpt37x.c
@@ -26,7 +26,7 @@
 #include <linux/libata.h>
 
 #define DRV_NAME	"pata_hpt37x"
-#define DRV_VERSION	"0.6.6"
+#define DRV_VERSION	"0.6.7"
 
 struct hpt_clock {
 	u8	xfer_speed;
@@ -1103,17 +1103,17 @@ static int hpt37x_init_one(struct pci_de
 
 		/* Select the DPLL clock. */
 		pci_write_config_byte(dev, 0x5b, 0x21);
-		pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low);
+		pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low | 0x100);
 
 		for(adjust = 0; adjust < 8; adjust++) {
 			if (hpt37x_calibrate_dpll(dev))
 				break;
 			/* See if it'll settle at a fractionally different clock */
-			if ((adjust & 3) == 3) {
-				f_low --;
-				f_high ++;
-			}
-			pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low);
+			if (adjust & 1)
+				f_low -= adjust >> 1;
+			else
+				f_high += adjust >> 1;
+			pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low | 0x100);
 		}
 		if (adjust == 8) {
 			printk(KERN_WARNING "hpt37x: DPLL did not stabilize.\n");
Index: linux-2.6.22/drivers/usb/storage/unusual_devs.h
===================================================================
--- linux-2.6.22.orig/drivers/usb/storage/unusual_devs.h
+++ linux-2.6.22/drivers/usb/storage/unusual_devs.h
@@ -313,6 +313,20 @@ UNUSUAL_DEV(  0x04b0, 0x0301, 0x0010, 0x
 		US_SC_DEVICE, US_PR_DEVICE,NULL,
 		US_FL_NOT_LOCKABLE ),
 
+/* Reported by Stefan de Konink <skinkie@xs4all.nl> */
+UNUSUAL_DEV(  0x04b0, 0x0401, 0x0200, 0x0200,
+		"NIKON",
+		"NIKON DSC D100",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_FIX_CAPACITY),
+
+/* Reported by Milinevsky Dmitry <niam.niam@gmail.com> */
+UNUSUAL_DEV(  0x04b0, 0x0409, 0x0100, 0x0100,
+		"NIKON",
+		"NIKON DSC D50",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_FIX_CAPACITY),
+
 /* Reported by Andreas Bockhold <andreas@bockionline.de> */
 UNUSUAL_DEV(  0x04b0, 0x0405, 0x0100, 0x0100,
 		"NIKON",
Index: linux-2.6.22/sound/usb/usbquirks.h
===================================================================
--- linux-2.6.22.orig/sound/usb/usbquirks.h
+++ linux-2.6.22/sound/usb/usbquirks.h
@@ -57,6 +57,15 @@
 		       USB_DEVICE_ID_MATCH_INT_CLASS |
 		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
 	.idVendor = 0x046d,
+	.idProduct = 0x08ae,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
+},
+{
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.idVendor = 0x046d,
 	.idProduct = 0x08f0,
 	.bInterfaceClass = USB_CLASS_AUDIO,
 	.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
Index: linux-2.6.22/arch/i386/kernel/cpu/cpufreq/Kconfig
===================================================================
--- linux-2.6.22.orig/arch/i386/kernel/cpu/cpufreq/Kconfig
+++ linux-2.6.22/arch/i386/kernel/cpu/cpufreq/Kconfig
@@ -90,10 +90,17 @@ config X86_POWERNOW_K8
 	  If in doubt, say N.
 
 config X86_POWERNOW_K8_ACPI
-	bool
-	depends on X86_POWERNOW_K8 && ACPI_PROCESSOR
-	depends on !(X86_POWERNOW_K8 = y && ACPI_PROCESSOR = m)
+	bool "ACPI Support"
+	select ACPI_PROCESSOR
+	depends on X86_POWERNOW_K8
 	default y
+	help
+	  This provides access to the K8s Processor Performance States via ACPI.
+	  This driver is probably required for CPUFreq to work with multi-socket and
+	  SMP systems.  It is not required on at least some single-socket yet
+	  multi-core systems, even if SMP is enabled.
+
+	  It is safe to say Y here.
 
 config X86_GX_SUSPMOD
 	tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation"
Index: linux-2.6.22/drivers/md/Kconfig
===================================================================
--- linux-2.6.22.orig/drivers/md/Kconfig
+++ linux-2.6.22/drivers/md/Kconfig
@@ -271,6 +271,17 @@ config DM_DELAY
 
 	If unsure, say N.
 
+config BLK_DEV_DM_BBR
+	tristate "Bad Block Relocation Device Target (EXPERIMENTAL)"
+	depends on BLK_DEV_DM && EXPERIMENTAL
+	---help---
+	  Support for devices with software-based bad-block-relocation.
+
+	  To compile this as a module, choose M here: the module will be
+	  called dm-bbr.
+
+	  If unsure, say N.
+
 endmenu
 
 endif
Index: linux-2.6.22/drivers/md/Makefile
===================================================================
--- linux-2.6.22.orig/drivers/md/Makefile
+++ linux-2.6.22/drivers/md/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_DM_MULTIPATH_EMC)	+= dm-emc
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
+obj-$(CONFIG_BLK_DEV_DM_BBR)	+= dm-bbr.o
 
 quiet_cmd_unroll = UNROLL  $@
       cmd_unroll = $(PERL) $(srctree)/$(src)/unroll.pl $(UNROLL) \
Index: linux-2.6.22/drivers/md/dm-bbr.c
===================================================================
--- /dev/null
+++ linux-2.6.22/drivers/md/dm-bbr.c
@@ -0,0 +1,1012 @@
+/*
+ *   (C) Copyright IBM Corp. 2002, 2004
+ *
+ *   This program is free software;  you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that 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.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program;  if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * linux/drivers/md/dm-bbr.c
+ *
+ * Bad-block-relocation (BBR) target for device-mapper.
+ *
+ * The BBR target is designed to remap I/O write failures to another safe
+ * location on disk. Note that most disk drives have BBR built into them,
+ * this means that our software BBR will be only activated when all hardware
+ * BBR replacement sectors have been used.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bio.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/mempool.h>
+#include <linux/workqueue.h>
+#include <linux/vmalloc.h>
+
+#include "dm.h"
+#include "dm-bio-list.h"
+#include "dm-bio-record.h"
+#include "dm-bbr.h"
+#include "dm-io.h"
+
+#define DM_MSG_PREFIX "bbr"
+#define SECTOR_SIZE (1 << SECTOR_SHIFT)
+
+static struct workqueue_struct *dm_bbr_wq = NULL;
+static void bbr_remap_handler(struct work_struct *work);
+static struct kmem_cache *bbr_remap_cache;
+static struct kmem_cache *bbr_io_cache;
+static mempool_t *bbr_io_pool;
+
+/**
+ * bbr_binary_tree_destroy
+ *
+ * Destroy the binary tree.
+ **/
+static void bbr_binary_tree_destroy(struct bbr_runtime_remap *root)
+{
+	struct bbr_runtime_remap **link = NULL;
+	struct bbr_runtime_remap *node = root;
+
+	while (node) {
+		if (node->left) {
+			link = &node->left;
+			node = node->left;
+			continue;
+		}
+		if (node->right) {
+			link = &node->right;
+			node = node->right;
+			continue;
+		}
+
+		kmem_cache_free(bbr_remap_cache, node);
+		if (node == root) {
+			/* If root is deleted, we're done. */
+			break;
+		}
+
+		/* Back to root. */
+		node = root;
+		*link = NULL;
+	}
+}
+
+static void bbr_free_remap(struct bbr_private *bbr_id)
+{
+	spin_lock_irq(&bbr_id->remap_root_lock);
+	bbr_binary_tree_destroy(bbr_id->remap_root);
+	bbr_id->remap_root = NULL;
+	spin_unlock_irq(&bbr_id->remap_root_lock);
+}
+
+static struct bbr_private *bbr_alloc_private(void)
+{
+	struct bbr_private *bbr_id;
+
+	bbr_id = kzalloc(sizeof(*bbr_id), GFP_KERNEL);
+	if (bbr_id == NULL)
+		return NULL;
+
+	INIT_WORK(&bbr_id->remap_work, bbr_remap_handler);
+	spin_lock_init(&bbr_id->remap_root_lock);
+	spin_lock_init(&bbr_id->remap_ios_lock);
+	bbr_id->in_use_replacement_blks = (atomic_t) ATOMIC_INIT(0);
+
+	return bbr_id;
+}
+
+static void bbr_free_private(struct bbr_private *bbr_id)
+{
+	vfree(bbr_id->bbr_table);
+	bbr_free_remap(bbr_id);
+	kfree(bbr_id);
+}
+
+static u32 crc_table[256];
+static u32 crc_table_built = 0;
+
+static void build_crc_table(void)
+{
+	u32 i, j, crc;
+
+	for (i = 0; i <= 255; i++) {
+		crc = i;
+		for (j = 8; j > 0; j--) {
+			if (crc & 1)
+				crc = (crc >> 1) ^ CRC_POLYNOMIAL;
+			else
+				crc >>= 1;
+		}
+		crc_table[i] = crc;
+	}
+	crc_table_built = 1;
+}
+
+static u32 calculate_crc(u32 crc, void *buffer, u32 buffersize)
+{
+	unsigned char *current_byte;
+	u32 temp1, temp2, i;
+
+	current_byte = (unsigned char *) buffer;
+	/* Make sure the crc table is available */
+	if (!crc_table_built)
+		build_crc_table();
+	/* Process each byte in the buffer. */
+	for (i = 0; i < buffersize; i++) {
+		temp1 = (crc >> 8) & 0x00FFFFFF;
+		temp2 = crc_table[(crc ^ (u32) * current_byte) &
+				  (u32) 0xff];
+		current_byte++;
+		crc = temp1 ^ temp2;
+	}
+	return crc;
+}
+
+/**
+ * le_bbr_table_sector_to_cpu
+ *
+ * Convert bbr meta data from on-disk (LE) format
+ * to the native cpu endian format.
+ **/
+static void le_bbr_table_sector_to_cpu(struct bbr_table *p)
+{
+	int i;
+	p->signature		= le32_to_cpup(&p->signature);
+	p->crc			= le32_to_cpup(&p->crc);
+	p->sequence_number	= le32_to_cpup(&p->sequence_number);
+	p->in_use_cnt		= le32_to_cpup(&p->in_use_cnt);
+	for (i = 0; i < BBR_ENTRIES_PER_SECT; i++) {
+		p->entries[i].bad_sect =
+			le64_to_cpup(&p->entries[i].bad_sect);
+		p->entries[i].replacement_sect =
+			le64_to_cpup(&p->entries[i].replacement_sect);
+	}
+}
+
+/**
+ * cpu_bbr_table_sector_to_le
+ *
+ * Convert bbr meta data from cpu endian format to on-disk (LE) format
+ **/
+static void cpu_bbr_table_sector_to_le(struct bbr_table *p,
+				       struct bbr_table *le)
+{
+	int i;
+	le->signature		= cpu_to_le32p(&p->signature);
+	le->crc			= cpu_to_le32p(&p->crc);
+	le->sequence_number	= cpu_to_le32p(&p->sequence_number);
+	le->in_use_cnt		= cpu_to_le32p(&p->in_use_cnt);
+	for (i = 0; i < BBR_ENTRIES_PER_SECT; i++) {
+		le->entries[i].bad_sect =
+			cpu_to_le64p(&p->entries[i].bad_sect);
+		le->entries[i].replacement_sect =
+			cpu_to_le64p(&p->entries[i].replacement_sect);
+	}
+}
+
+/**
+ * validate_bbr_table_sector
+ *
+ * Check the specified BBR table sector for a valid signature and CRC. If it's
+ * valid, endian-convert the table sector.
+ **/
+static int validate_bbr_table_sector(struct bbr_table *p)
+{
+	int org_crc, final_crc;
+
+	if (le32_to_cpup(&p->signature) != BBR_TABLE_SIGNATURE) {
+		DMERR("BBR table signature doesn't match!");
+		DMERR("Found 0x%x. Expecting 0x%x",
+		      le32_to_cpup(&p->signature), BBR_TABLE_SIGNATURE);
+		return -EINVAL;
+	}
+
+	if (!p->crc) {
+		DMERR("BBR table sector has no CRC!");
+		return -EINVAL;
+	}
+
+	org_crc = le32_to_cpup(&p->crc);
+	p->crc = 0;
+	final_crc = calculate_crc(INITIAL_CRC, (void *)p, sizeof(*p));
+	if (final_crc != org_crc) {
+		DMERR("CRC failed!");
+		DMERR("Found 0x%x. Expecting 0x%x",
+		      org_crc, final_crc);
+		return -EINVAL;
+	}
+
+	p->crc = cpu_to_le32p(&org_crc);
+	le_bbr_table_sector_to_cpu(p);
+
+	return 0;
+}
+
+/**
+ * bbr_binary_tree_insert
+ *
+ * Insert a node into the binary tree.
+ **/
+static void bbr_binary_tree_insert(struct bbr_runtime_remap **root,
+				   struct bbr_runtime_remap *newnode)
+{
+	struct bbr_runtime_remap **node = root;
+	while (node && *node) {
+		node = (newnode->remap.bad_sect > (*node)->remap.bad_sect) ?
+			&(*node)->right : &(*node)->left;
+	}
+
+	newnode->left = newnode->right = NULL;
+	*node = newnode;
+}
+
+/**
+ * bbr_binary_search
+ *
+ * Search for a node that contains bad_sect == lsn.
+ **/
+static struct bbr_runtime_remap *bbr_binary_search(
+	struct bbr_runtime_remap *root,
+	u64 lsn)
+{
+	struct bbr_runtime_remap *node = root;
+	while (node) {
+		if (node->remap.bad_sect == lsn)
+			break;
+
+		node = (lsn > node->remap.bad_sect) ? node->right : node->left;
+	}
+	return node;
+}
+
+/**
+ * bbr_insert_remap_entry
+ *
+ * Create a new remap entry and add it to the binary tree for this node.
+ **/
+static int bbr_insert_remap_entry(struct bbr_private *bbr_id,
+				  struct bbr_table_entry *new_bbr_entry)
+{
+	struct bbr_runtime_remap *newnode;
+
+	newnode = kmem_cache_alloc(bbr_remap_cache, GFP_NOIO);
+	if (!newnode) {
+		DMERR("Could not allocate from remap cache!");
+		return -ENOMEM;
+	}
+	newnode->remap.bad_sect = new_bbr_entry->bad_sect;
+	newnode->remap.replacement_sect = new_bbr_entry->replacement_sect;
+	spin_lock_irq(&bbr_id->remap_root_lock);
+	bbr_binary_tree_insert(&bbr_id->remap_root, newnode);
+	spin_unlock_irq(&bbr_id->remap_root_lock);
+	return 0;
+}
+
+/**
+ * bbr_table_to_remap_list
+ *
+ * The on-disk bbr table is sorted by the replacement sector LBA. In order to
+ * improve run time performance, the in memory remap list must be sorted by
+ * the bad sector LBA. This function is called at discovery time to initialize
+ * the remap list. This function assumes that at least one copy of meta data
+ * is valid.
+ **/
+static u32 bbr_table_to_remap_list(struct bbr_private *bbr_id)
+{
+	u32 in_use_blks = 0;
+	int i, j;
+	struct bbr_table *p;
+
+	for (i = 0, p = bbr_id->bbr_table;
+	     i < bbr_id->nr_sects_bbr_table;
+	     i++, p++) {
+		if (!p->in_use_cnt)
+			break;
+
+		in_use_blks += p->in_use_cnt;
+		for (j = 0; j < p->in_use_cnt; j++)
+			bbr_insert_remap_entry(bbr_id, &p->entries[j]);
+	}
+	if (in_use_blks) {
+		char b[32];
+		DMWARN("There are %u BBR entries for device %s",
+		       in_use_blks, format_dev_t(b, bbr_id->dev->bdev->bd_dev));
+	}
+
+	return in_use_blks;
+}
+
+/**
+ * bbr_search_remap_entry
+ *
+ * Search remap entry for the specified sector. If found, return a pointer to
+ * the table entry. Otherwise, return NULL.
+ **/
+static struct bbr_table_entry *bbr_search_remap_entry(
+	struct bbr_private *bbr_id,
+	u64 lsn)
+{
+	struct bbr_runtime_remap *p;
+
+	spin_lock_irq(&bbr_id->remap_root_lock);
+	p = bbr_binary_search(bbr_id->remap_root, lsn);
+	spin_unlock_irq(&bbr_id->remap_root_lock);
+	return (p) ? &p->remap : NULL;
+}
+
+/**
+ * bbr_remap
+ *
+ * If *lsn is in the remap table, return TRUE and modify *lsn,
+ * else, return FALSE.
+ **/
+static int bbr_remap(struct bbr_private *bbr_id,
+			    u64 *lsn)
+{
+	struct bbr_table_entry *e;
+
+	if (atomic_read(&bbr_id->in_use_replacement_blks)) {
+		e = bbr_search_remap_entry(bbr_id, *lsn);
+		if (e) {
+			*lsn = e->replacement_sect;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/**
+ * bbr_remap_probe
+ *
+ * If any of the sectors in the range [lsn, lsn+nr_sects] are in the remap
+ * table return TRUE, Else, return FALSE.
+ **/
+static int bbr_remap_probe(struct bbr_private *bbr_id,
+				  u64 lsn, u64 nr_sects)
+{
+	u64 tmp, cnt;
+
+	if (atomic_read(&bbr_id->in_use_replacement_blks)) {
+		for (cnt = 0, tmp = lsn;
+		     cnt < nr_sects;
+		     cnt += bbr_id->blksize_in_sects, tmp = lsn + cnt) {
+			if (bbr_remap(bbr_id,&tmp))
+				return 1;
+		}
+	}
+	return 0;
+}
+
+static int rw_table(struct bbr_private *bbr_id, void *vma,
+		    struct io_region *ptr, int rw)
+{
+	bbr_id->vma_io_req.bi_rw = rw;
+	bbr_id->vma_io_req.mem.ptr.vma = vma;
+	bbr_id->vma_io_req.notify.fn = NULL;
+
+	return dm_io(&bbr_id->vma_io_req, 1, ptr, NULL);
+}
+
+static int io_sync(struct bbr_private *bbr_id, struct page_list *pl,
+		   unsigned offset, struct io_region *ptr, int rw)
+{
+	bbr_id->page_io_req.bi_rw = rw;
+	bbr_id->page_io_req.mem.ptr.pl = pl;
+	bbr_id->page_io_req.mem.offset = offset;
+	bbr_id->page_io_req.notify.fn = NULL;
+
+	return dm_io(&bbr_id->page_io_req, 1, ptr, NULL);
+}
+
+/**
+ * bbr_setup
+ *
+ * Read the remap tables from disk and set up the initial remap tree.
+ **/
+static int bbr_setup(struct bbr_private *bbr_id)
+{
+	struct bbr_table *table = bbr_id->bbr_table;
+	struct io_region job;
+	int i, rc = 0;
+
+	job.bdev = bbr_id->dev->bdev;
+	job.count = 1;
+
+	/* Read and verify each BBR table sector individually. */
+	for (i = 0; i < bbr_id->nr_sects_bbr_table; i++, table++) {
+		job.sector = bbr_id->lba_table1 + i;
+		rc = rw_table(bbr_id, table, &job, READ);
+		if (rc && bbr_id->lba_table2) {
+			job.sector = bbr_id->lba_table2 + i;
+			rc = rw_table(bbr_id, table, &job, READ);
+		}
+		if (rc)
+			goto out;
+
+		rc = validate_bbr_table_sector(table);
+		if (rc)
+			goto out;
+	}
+	atomic_set(&bbr_id->in_use_replacement_blks,
+		   bbr_table_to_remap_list(bbr_id));
+
+out:
+	if (rc)
+		DMERR("error during device setup: %d", rc);
+	return rc;
+}
+
+/**
+ * bbr_io_remap_error
+ * @bbr_id:		Private data for the BBR node.
+ * @rw:			READ or WRITE.
+ * @starting_lsn:	Starting sector of request to remap.
+ * @count:		Number of sectors in the request.
+ * @page:		Page containing the data for the request.
+ * @offset:		Byte-offset of the data within the page.
+ *
+ * For the requested range, try to write each sector individually. For each
+ * sector that fails, find the next available remap location and write the
+ * data to that new location. Then update the table and write both copies
+ * of the table to disk. Finally, update the in-memory mapping and do any
+ * other necessary bookkeeping.
+ **/
+static int bbr_io_remap_error(struct bbr_private *bbr_id,
+			      int rw,
+			      u64 starting_lsn,
+			      u64 count,
+			      struct page *page,
+			      unsigned int offset)
+{
+	struct bbr_table *bbr_table;
+	struct io_region job;
+	struct page_list pl;
+	unsigned long table_sector_index;
+	unsigned long table_sector_offset;
+	unsigned long index;
+	u64 lsn, new_lsn;
+	char b[32];
+	int rc;
+
+	job.bdev = bbr_id->dev->bdev;
+	job.count = 1;
+	pl.page = page;
+	pl.next = NULL;
+
+	/* For each sector in the request. */
+	for (lsn = 0; lsn < count; lsn++, offset += SECTOR_SIZE) {
+		job.sector = starting_lsn + lsn;
+		rc = io_sync(bbr_id, &pl, offset, &job, rw);
+		while (rc) {
+			/* Find the next available relocation sector. */
+			new_lsn = atomic_read(&bbr_id->in_use_replacement_blks);
+			if (new_lsn >= bbr_id->nr_replacement_blks) {
+				/* No more replacement sectors available. */
+				return -EIO;
+			}
+			new_lsn += bbr_id->start_replacement_sect;
+
+			/* Write the data to its new location. */
+			DMWARN("device %s: Trying to remap bad sector "PFU64" to sector "PFU64,
+			       format_dev_t(b, bbr_id->dev->bdev->bd_dev),
+			       starting_lsn + lsn, new_lsn);
+			job.sector = new_lsn;
+			rc = io_sync(bbr_id, &pl, offset, &job, rw);
+			if (rc) {
+				/* This replacement sector is bad.
+				 * Try the next one.
+				 */
+				DMERR("device %s: replacement sector "PFU64" is bad. Skipping.",
+				      format_dev_t(b, bbr_id->dev->bdev->bd_dev), new_lsn);
+				atomic_inc(&bbr_id->in_use_replacement_blks);
+				continue;
+			}
+
+			/* Add this new entry to the on-disk table. */
+			table_sector_index = new_lsn -
+					     bbr_id->start_replacement_sect;
+			table_sector_offset = table_sector_index /
+					      BBR_ENTRIES_PER_SECT;
+			index = table_sector_index % BBR_ENTRIES_PER_SECT;
+
+			bbr_table = &bbr_id->bbr_table[table_sector_offset];
+			bbr_table->entries[index].bad_sect = starting_lsn + lsn;
+			bbr_table->entries[index].replacement_sect = new_lsn;
+			bbr_table->in_use_cnt++;
+			bbr_table->sequence_number++;
+			bbr_table->crc = 0;
+			bbr_table->crc = calculate_crc(INITIAL_CRC,
+						       bbr_table,
+						       sizeof(struct bbr_table));
+
+			/* Write the table to disk. */
+			cpu_bbr_table_sector_to_le(bbr_table, bbr_table);
+			if (bbr_id->lba_table1) {
+				job.sector = bbr_id->lba_table1 + table_sector_offset;
+				rc = rw_table(bbr_id, bbr_table, &job, WRITE);
+			}
+			if (bbr_id->lba_table2) {
+				job.sector = bbr_id->lba_table2 + table_sector_offset;
+				rc |= rw_table(bbr_id, bbr_table, &job, WRITE);
+			}
+			le_bbr_table_sector_to_cpu(bbr_table);
+
+			if (rc) {
+				/* Error writing one of the tables to disk. */
+				DMERR("device %s: error updating BBR tables on disk.",
+				      format_dev_t(b, bbr_id->dev->bdev->bd_dev));
+				return rc;
+			}
+
+			/* Insert a new entry in the remapping binary-tree. */
+			rc = bbr_insert_remap_entry(bbr_id,
+						    &bbr_table->entries[index]);
+			if (rc) {
+				DMERR("device %s: error adding new entry to remap tree.",
+				      format_dev_t(b, bbr_id->dev->bdev->bd_dev));
+				return rc;
+			}
+
+			atomic_inc(&bbr_id->in_use_replacement_blks);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * bbr_io_process_request
+ *
+ * For each sector in this request, check if the sector has already
+ * been remapped. If so, process all previous sectors in the request,
+ * followed by the remapped sector. Then reset the starting lsn and
+ * count, and keep going with the rest of the request as if it were
+ * a whole new request. If any of the sync_io's return an error,
+ * call the remapper to relocate the bad sector(s).
+ *
+ * 2.5 Note: When switching over to bio's for the I/O path, we have made
+ * the assumption that the I/O request described by the bio is one
+ * virtually contiguous piece of memory (even though the bio vector
+ * describes it using a series of physical page addresses).
+ **/
+static int bbr_io_process_request(struct bbr_private *bbr_id,
+				  struct bio *bio)
+{
+	struct io_region job;
+	u64 starting_lsn = bio->bi_sector;
+	u64 count, lsn, remapped_lsn;
+	struct page_list pl;
+	unsigned int offset;
+	int i, rw = bio_data_dir(bio);
+	int rc = 0;
+
+	job.bdev = bbr_id->dev->bdev;
+	pl.next = NULL;
+
+	/* Each bio can contain multiple vectors, each with a different page.
+	 * Treat each vector as a separate request.
+	 */
+	/* KMC: Is this the right way to walk the bvec list? */
+	for (i = 0;
+	     i < bio->bi_vcnt;
+	     i++, bio->bi_idx++, starting_lsn += count) {
+
+		/* Bvec info: number of sectors, page,
+		 * and byte-offset within page.
+		 */
+		count = bio_iovec(bio)->bv_len >> SECTOR_SHIFT;
+		pl.page = bio_iovec(bio)->bv_page;
+		offset = bio_iovec(bio)->bv_offset;
+
+		/* For each sector in this bvec, check if the sector has
+		 * already been remapped. If so, process all previous sectors
+		 * in this request, followed by the remapped sector. Then reset
+		 * the starting lsn and count and keep going with the rest of
+		 * the request as if it were a whole new request.
+		 */
+		for (lsn = 0; lsn < count; lsn++) {
+			remapped_lsn = starting_lsn + lsn;
+			rc = bbr_remap(bbr_id, &remapped_lsn);
+			if (!rc) {
+				/* This sector is fine. */
+				continue;
+			}
+
+			/* Process all sectors in the request up to this one. */
+			if (lsn > 0) {
+				job.sector = starting_lsn;
+				job.count = lsn;
+				rc = io_sync(bbr_id, &pl, offset, &job, rw);
+				if (rc) {
+					/* If this I/O failed, then one of the
+					 * sectors in this request needs to be
+					 * relocated.
+					 */
+					rc = bbr_io_remap_error(bbr_id, rw,
+								starting_lsn,
+								lsn, pl.page,
+								offset);
+					if (rc) {
+						/* KMC: Return? Or continue to next bvec? */
+						return rc;
+					}
+				}
+				offset += (lsn << SECTOR_SHIFT);
+			}
+
+			/* Process the remapped sector. */
+			job.sector = remapped_lsn;
+			job.count = 1;
+			rc = io_sync(bbr_id, &pl, offset, &job, rw);
+			if (rc) {
+				/* BUGBUG - Need more processing if this caused
+				 * an error. If this I/O failed, then the
+				 * existing remap is now bad, and we need to
+				 * find a new remap. Can't use
+				 * bbr_io_remap_error(), because the existing
+				 * map entry needs to be changed, not added
+				 * again, and the original table entry also
+				 * needs to be changed.
+				 */
+				return rc;
+			}
+
+			starting_lsn	+= (lsn + 1);
+			count		-= (lsn + 1);
+			lsn		= -1;
+			offset		+= SECTOR_SIZE;
+		}
+
+		/* Check for any remaining sectors after the last split. This
+		 * could potentially be the whole request, but that should be a
+		 * rare case because requests should only be processed by the
+		 * thread if we know an error occurred or they contained one or
+		 * more remapped sectors.
+		 */
+		if (count) {
+			job.sector = starting_lsn;
+			job.count = count;
+			rc = io_sync(bbr_id, &pl, offset, &job, rw);
+			if (rc) {
+				/* If this I/O failed, then one of the sectors
+				 * in this request needs to be relocated.
+				 */
+				rc = bbr_io_remap_error(bbr_id, rw, starting_lsn,
+							count, pl.page, offset);
+				if (rc) {
+					/* KMC: Return? Or continue to next bvec? */
+					return rc;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void bbr_io_process_requests(struct bbr_private *bbr_id,
+				    struct bio *bio)
+{
+	struct bio *next;
+	int rc;
+
+	while (bio) {
+		next = bio->bi_next;
+		bio->bi_next = NULL;
+
+		rc = bbr_io_process_request(bbr_id, bio);
+
+		bio_endio(bio, bio->bi_size, rc);
+
+		bio = next;
+	}
+}
+
+/**
+ * bbr_remap_handler
+ *
+ * This is the handler for the bbr work-queue.
+ *
+ * I/O requests should only be sent to this handler if we know that:
+ * a) the request contains at least one remapped sector.
+ *   or
+ * b) the request caused an error on the normal I/O path.
+ *
+ * This function uses synchronous I/O, so sending a request to this
+ * thread that doesn't need special processing will cause severe
+ * performance degredation.
+ **/
+static void bbr_remap_handler(struct work_struct *work)
+{
+	struct bbr_private *bbr_id =
+		container_of(work, struct bbr_private, remap_work);
+	struct bio *bio;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bbr_id->remap_ios_lock, flags);
+	bio = bio_list_get(&bbr_id->remap_ios);
+	spin_unlock_irqrestore(&bbr_id->remap_ios_lock, flags);
+
+	bbr_io_process_requests(bbr_id, bio);
+}
+
+/**
+ * bbr_endio
+ *
+ * This is the callback for normal write requests. Check for an error
+ * during the I/O, and send to the thread for processing if necessary.
+ **/
+static int bbr_endio(struct dm_target *ti, struct bio *bio,
+		     int error, union map_info *map_context)
+{
+	struct bbr_private *bbr_id = ti->private;
+	struct dm_bio_details *bbr_io = map_context->ptr;
+
+	if (error && bbr_io) {
+		unsigned long flags;
+		char b[32];
+
+		dm_bio_restore(bbr_io, bio);
+		map_context->ptr = NULL;
+
+		DMERR("device %s: I/O failure on sector %lu. "
+		      "Scheduling for retry.",
+		      format_dev_t(b, bbr_id->dev->bdev->bd_dev),
+		      (unsigned long)bio->bi_sector);
+
+		spin_lock_irqsave(&bbr_id->remap_ios_lock, flags);
+		bio_list_add(&bbr_id->remap_ios, bio);
+		spin_unlock_irqrestore(&bbr_id->remap_ios_lock, flags);
+
+		queue_work(dm_bbr_wq, &bbr_id->remap_work);
+
+		error = 1;
+	}
+
+	if (bbr_io)
+		mempool_free(bbr_io, bbr_io_pool);
+
+	return error;
+}
+
+/**
+ * Construct a bbr mapping
+ **/
+static int bbr_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct bbr_private *bbr_id;
+	unsigned long block_size;
+	char *end;
+	int rc = -EINVAL;
+
+	if (argc != 8) {
+		ti->error = "dm-bbr requires exactly 8 arguments: "
+			    "device offset table1_lsn table2_lsn table_size start_replacement nr_replacement_blks block_size";
+		goto out1;
+	}
+
+	bbr_id = bbr_alloc_private();
+	if (!bbr_id) {
+		ti->error = "dm-bbr: Error allocating bbr private data.";
+		goto out1;
+	}
+
+	bbr_id->offset = simple_strtoull(argv[1], &end, 10);
+	bbr_id->lba_table1 = simple_strtoull(argv[2], &end, 10);
+	bbr_id->lba_table2 = simple_strtoull(argv[3], &end, 10);
+	bbr_id->nr_sects_bbr_table = simple_strtoull(argv[4], &end, 10);
+	bbr_id->start_replacement_sect = simple_strtoull(argv[5], &end, 10);
+	bbr_id->nr_replacement_blks = simple_strtoull(argv[6], &end, 10);
+	block_size = simple_strtoul(argv[7], &end, 10);
+	bbr_id->blksize_in_sects = (block_size >> SECTOR_SHIFT);
+
+	bbr_id->vma_io_req.mem.type = DM_IO_VMA;
+	bbr_id->vma_io_req.client = dm_io_client_create(1);
+	if (IS_ERR(bbr_id->vma_io_req.client)) {
+		rc = PTR_ERR(bbr_id->vma_io_req.client);
+		DMWARN("couldn't allocate disk VMA io client");
+		goto out2;
+	}
+
+	bbr_id->page_io_req.mem.type = DM_IO_PAGE_LIST;
+	bbr_id->page_io_req.client = dm_io_client_create(1);
+	if (IS_ERR(bbr_id->page_io_req.client)) {
+		rc = PTR_ERR(bbr_id->page_io_req.client);
+		DMWARN("couldn't allocate pagelist io client");
+		goto out3;
+	}
+
+	bbr_id->bbr_table = vmalloc(bbr_id->nr_sects_bbr_table << SECTOR_SHIFT);
+	if (!bbr_id->bbr_table) {
+		ti->error = "dm-bbr: Error allocating bbr table.";
+		goto out4;
+	}
+
+	if (dm_get_device(ti, argv[0], 0, ti->len,
+			  dm_table_get_mode(ti->table), &bbr_id->dev)) {
+		ti->error = "dm-bbr: Device lookup failed";
+		goto out4;
+	}
+
+	rc = bbr_setup(bbr_id);
+	if (rc) {
+		ti->error = "dm-bbr: Device setup failed";
+		goto out5;
+	}
+
+	ti->private = bbr_id;
+	return 0;
+
+out5:
+	dm_put_device(ti, bbr_id->dev);
+out4:
+	dm_io_client_destroy(bbr_id->page_io_req.client);
+out3:
+	dm_io_client_destroy(bbr_id->vma_io_req.client);
+out2:
+	bbr_free_private(bbr_id);
+out1:
+	return rc;
+}
+
+static void bbr_dtr(struct dm_target *ti)
+{
+	struct bbr_private *bbr_id = ti->private;
+
+	dm_put_device(ti, bbr_id->dev);
+	dm_io_client_destroy(bbr_id->page_io_req.client);
+	dm_io_client_destroy(bbr_id->vma_io_req.client);
+	bbr_free_private(bbr_id);
+}
+
+static int bbr_map(struct dm_target *ti, struct bio *bio,
+		   union map_info *map_context)
+{
+	struct bbr_private *bbr_id = ti->private;
+	struct dm_bio_details *bbr_io;
+	unsigned long flags;
+	int rc = 1;
+
+	bio->bi_sector += bbr_id->offset;
+
+	if (atomic_read(&bbr_id->in_use_replacement_blks) == 0 ||
+	    !bbr_remap_probe(bbr_id, bio->bi_sector, bio_sectors(bio))) {
+		/* No existing remaps or this request doesn't
+		 * contain any remapped sectors.
+		 */
+		bio->bi_bdev = bbr_id->dev->bdev;
+
+		bbr_io = mempool_alloc(bbr_io_pool, GFP_NOIO);
+		dm_bio_record(bbr_io, bio);
+		map_context->ptr = bbr_io;
+	} else {
+		/* This request has at least one remapped sector.
+		 * Give it to the work-queue for processing.
+		 */
+		map_context->ptr = NULL;
+		spin_lock_irqsave(&bbr_id->remap_ios_lock, flags);
+		bio_list_add(&bbr_id->remap_ios, bio);
+		spin_unlock_irqrestore(&bbr_id->remap_ios_lock, flags);
+
+		queue_work(dm_bbr_wq, &bbr_id->remap_work);
+		rc = 0;
+	}
+
+	return rc;
+}
+
+static int bbr_status(struct dm_target *ti, status_type_t type,
+		      char *result, unsigned int maxlen)
+{
+	struct bbr_private *bbr_id = ti->private;
+	char b[BDEVNAME_SIZE];
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		snprintf(result, maxlen, "%s "PFU64" "PFU64" "PFU64" "PFU64" "PFU64" "PFU64" %u",
+			 format_dev_t(b, bbr_id->dev->bdev->bd_dev),
+			 bbr_id->offset, bbr_id->lba_table1, bbr_id->lba_table2,
+			 bbr_id->nr_sects_bbr_table,
+			 bbr_id->start_replacement_sect,
+			 bbr_id->nr_replacement_blks,
+			 bbr_id->blksize_in_sects << SECTOR_SHIFT);
+		 break;
+	}
+	return 0;
+}
+
+static struct target_type bbr_target = {
+	.name	= "bbr",
+	.version= {1, 0, 1},
+	.module	= THIS_MODULE,
+	.ctr	= bbr_ctr,
+	.dtr	= bbr_dtr,
+	.map	= bbr_map,
+	.end_io	= bbr_endio,
+	.status	= bbr_status,
+};
+
+int __init dm_bbr_init(void)
+{
+	int rc;
+
+	rc = dm_register_target(&bbr_target);
+	if (rc) {
+		DMERR("error registering target.");
+		goto err1;
+	}
+
+	bbr_remap_cache = kmem_cache_create("bbr-remap",
+					    sizeof(struct bbr_runtime_remap),
+					    0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!bbr_remap_cache) {
+		DMERR("error creating remap cache.");
+		rc = ENOMEM;
+		goto err2;
+	}
+
+	bbr_io_cache = kmem_cache_create("bbr-io", sizeof(struct dm_bio_details),
+					 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!bbr_io_cache) {
+		DMERR("error creating io cache.");
+		rc = ENOMEM;
+		goto err3;
+	}
+
+	bbr_io_pool = mempool_create(256, mempool_alloc_slab,
+				     mempool_free_slab, bbr_io_cache);
+	if (!bbr_io_pool) {
+		DMERR("error creating io mempool.");
+		rc = ENOMEM;
+		goto err4;
+	}
+
+	dm_bbr_wq = create_workqueue("dm-bbr");
+	if (!dm_bbr_wq) {
+		DMERR("error creating work-queue.");
+		rc = ENOMEM;
+		goto err5;
+	}
+
+	return 0;
+
+err5:
+	mempool_destroy(bbr_io_pool);
+err4:
+	kmem_cache_destroy(bbr_io_cache);
+err3:
+	kmem_cache_destroy(bbr_remap_cache);
+err2:
+	dm_unregister_target(&bbr_target);
+err1:
+	return rc;
+}
+
+void __exit dm_bbr_exit(void)
+{
+	destroy_workqueue(dm_bbr_wq);
+	mempool_destroy(bbr_io_pool);
+	kmem_cache_destroy(bbr_io_cache);
+	kmem_cache_destroy(bbr_remap_cache);
+	dm_unregister_target(&bbr_target);
+}
+
+module_init(dm_bbr_init);
+module_exit(dm_bbr_exit);
+MODULE_LICENSE("GPL");
Index: linux-2.6.22/drivers/md/dm-bbr.h
===================================================================
--- /dev/null
+++ linux-2.6.22/drivers/md/dm-bbr.h
@@ -0,0 +1,130 @@
+/*
+ *   (C) Copyright IBM Corp. 2002, 2004
+ *
+ *   This program is free software;  you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that 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.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program;  if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * linux/drivers/md/dm-bbr.h
+ *
+ * Bad-block-relocation (BBR) target for device-mapper.
+ *
+ * The BBR target is designed to remap I/O write failures to another safe
+ * location on disk. Note that most disk drives have BBR built into them,
+ * this means that our software BBR will be only activated when all hardware
+ * BBR replacement sectors have been used.
+ */
+
+#include "dm-io.h"
+
+#define BBR_TABLE_SIGNATURE		0x42627254 /* BbrT */
+#define BBR_ENTRIES_PER_SECT		31
+#define INITIAL_CRC			0xFFFFFFFF
+#define CRC_POLYNOMIAL			0xEDB88320L
+
+/**
+ * Macros to cleanly print 64-bit numbers on both 32-bit and 64-bit machines.
+ * Use these in place of %Ld, %Lu, and %Lx.
+ **/
+#if BITS_PER_LONG > 32
+#define PFU64 "%lu"
+#else
+#define PFU64 "%Lu"
+#endif
+
+/**
+ * struct bbr_table_entry
+ * @bad_sect:		LBA of bad location.
+ * @replacement_sect:	LBA of new location.
+ *
+ * Structure to describe one BBR remap.
+ **/
+struct bbr_table_entry {
+	u64 bad_sect;
+	u64 replacement_sect;
+};
+
+/**
+ * struct bbr_table
+ * @signature:		Signature on each BBR table sector.
+ * @crc:		CRC for this table sector.
+ * @sequence_number:	Used to resolve conflicts when primary and secondary
+ *			tables do not match.
+ * @in_use_cnt:		Number of in-use table entries.
+ * @entries:		Actual table of remaps.
+ *
+ * Structure to describe each sector of the metadata table. Each sector in this
+ * table can describe 31 remapped sectors.
+ **/
+struct bbr_table {
+	u32			signature;
+	u32			crc;
+	u32			sequence_number;
+	u32			in_use_cnt;
+	struct bbr_table_entry	entries[BBR_ENTRIES_PER_SECT];
+};
+
+/**
+ * struct bbr_runtime_remap
+ *
+ * Node in the binary tree used to keep track of remaps.
+ **/
+struct bbr_runtime_remap {
+	struct bbr_table_entry		remap;
+	struct bbr_runtime_remap	*left;
+	struct bbr_runtime_remap	*right;
+};
+
+/**
+ * struct bbr_private
+ * @dev:			Info about underlying device.
+ * @bbr_table:			Copy of metadata table.
+ * @remap_root:			Binary tree containing all remaps.
+ * @remap_root_lock:		Lock for the binary tree.
+ * @remap_work:			For adding work items to the work-queue.
+ * @remap_ios:			List of I/Os for the work-queue to handle.
+ * @remap_ios_lock:		Lock for the remap_ios list.
+ * @offset:			LBA of data area.
+ * @lba_table1:			LBA of primary BBR table.
+ * @lba_table2:			LBA of secondary BBR table.
+ * @nr_sects_bbr_table:		Size of each BBR table.
+ * @nr_replacement_blks:	Number of replacement blocks.
+ * @start_replacement_sect:	LBA of start of replacement blocks.
+ * @blksize_in_sects:		Size of each block.
+ * @in_use_replacement_blks:	Current number of remapped blocks.
+ *
+ * Private data for each BBR target.
+ **/
+struct bbr_private {
+	struct dm_dev			*dev;
+	struct bbr_table		*bbr_table;
+	struct bbr_runtime_remap	*remap_root;
+	spinlock_t			remap_root_lock;
+
+	struct dm_io_request vma_io_req;
+	struct dm_io_request page_io_req;
+
+	struct work_struct		remap_work;
+	struct bio_list			remap_ios;
+	spinlock_t			remap_ios_lock;
+
+	u64				offset;
+	u64				lba_table1;
+	u64				lba_table2;
+	u64				nr_sects_bbr_table;
+	u64				start_replacement_sect;
+	u64				nr_replacement_blks;
+	u32				blksize_in_sects;
+	atomic_t			in_use_replacement_blks;
+};
+
Index: linux-2.6.22/Documentation/fb/00-INDEX
===================================================================
--- linux-2.6.22.orig/Documentation/fb/00-INDEX
+++ linux-2.6.22/Documentation/fb/00-INDEX
@@ -19,6 +19,8 @@ matroxfb.txt
 	- info on the Matrox frame buffer driver
 pvr2fb.txt
 	- info on the PowerVR 2 frame buffer driver
+splash.txt
+	- info on the Framebuffer Splash
 tgafb.txt
 	- info on the TGA (DECChip 21030) frame buffer driver
 vesafb.txt
Index: linux-2.6.22/Documentation/fb/splash.txt
===================================================================
--- /dev/null
+++ linux-2.6.22/Documentation/fb/splash.txt
@@ -0,0 +1,207 @@
+What is it?
+-----------
+
+The framebuffer splash is a kernel feature that allows displaying a background
+picture on selected consoles.
+
+What do I need to get it to work?
+---------------------------------
+
+To get fb splash up-and-running you will have to:
+ 1) get a copy of splashutils [1] or a similar program
+ 2) get some splash themes
+ 3) build the kernel helper program
+ 4) build your kernel with the FB_SPLASH option enabled.
+
+To get fbsplash operational right after fbcon initialization is finished, you
+will have to include a theme and the kernel helper into your initramfs image.
+Please refer to splashutils documentation for instructions on how to do that.
+
+[1] The splashutils package can be downloaded from:
+    http://dev.gentoo.org/~spock/projects/splashutils/
+
+The userspace helper
+--------------------
+
+The userspace splash helper (by default: /sbin/splash_helper) is called by the
+kernel whenever an important event occurs and the kernel needs some kind of
+job to be carried out. Important events include console switches and video
+mode switches (the kernel requests background images and configuration
+parameters for the current console). The splash helper must be accessible at
+all times. If it's not, fbsplash will be switched off automatically.
+
+It's possible to set path to the splash helper by writing it to
+/proc/sys/kernel/fbsplash.
+
+*****************************************************************************
+
+The information below is mostly technical stuff. There's probably no need to
+read it unless you plan to develop a userspace helper.
+
+The splash protocol
+-------------------
+
+The splash protocol defines a communication interface between the kernel and
+the userspace splash helper.
+
+The kernel side is responsible for:
+
+ * rendering console text, using an image as a background (instead of a
+   standard solid color fbcon uses),
+ * accepting commands from the user via ioctls on the fbsplash device,
+ * calling the userspace helper to set things up as soon as the fb subsystem 
+   is initialized.
+
+The userspace helper is responsible for everything else, including parsing
+configuration files, decompressing the image files whenever the kernel needs
+it, and communicating with the kernel if necessary.
+
+The splash protocol specifies how communication is done in both ways:
+kernel->userspace and userspace->helper.
+  
+Kernel -> Userspace
+-------------------
+
+The kernel communicates with the userspace helper by calling it and specifying
+the task to be done in a series of arguments.
+
+The arguments follow the pattern:
+<splash protocol version> <command> <parameters>
+
+All commands defined in splash protocol v2 have the following parameters:
+ virtual console
+ framebuffer number
+ theme
+
+Splash protocol v1 specified an additional 'fbsplash mode' after the
+framebuffer number. Splash protocol v1 is deprecated and should not be used.
+
+Splash protocol v2 specifies the following commands:
+
+getpic
+------
+ The kernel issues this command to request image data. It's up to the 
+ userspace  helper to find a background image appropriate for the specified 
+ theme and the current resolution. The userspace helper should respond by 
+ issuing the FBIOSPLASH_SETPIC ioctl.
+
+init
+----
+ The kernel issues this command after the fbsplash device is created and
+ the fbsplash interface is initialized. Upon receiving 'init', the userspace
+ helper should parse the kernel command line (/proc/cmdline) or otherwise
+ decide whether fbsplash is to be activated.
+
+ To activate fbsplash on the first console the helper should issue the
+ FBIOSPLASH_SETCFG, FBIOSPLASH_SETPIC and FBIOSPLASH_SETSTATE commands,
+ in the above-mentioned order.
+
+ When the userspace helper is called in an early phase of the boot process
+ (right after the initialization of fbcon), no filesystems will be mounted.
+ The helper program should mount sysfs and then create the appropriate
+ framebuffer, fbsplash and tty0 devices (if they don't already exist) to get
+ current display settings and to be able to communicate with the kernel side.
+ It should probably also mount the procfs to be able to parse the kernel
+ command line parameters.
+
+ Note that the console sem is not held when the kernel calls splash_helper
+ with the 'init' command. The splash helper should perform all ioctls with
+ origin set to FB_SPLASH_IO_ORIG_USER.
+
+modechange
+----------
+ The kernel issues this command on a mode change. The helper's response should
+ be similar to the response to the 'init' command. Note that this time the
+ console sem is held and all ioctls must be performed with origin set to
+ FB_SPLASH_IO_ORIG_KERNEL.
+
+
+Userspace -> Kernel
+-------------------
+
+Userspace programs can communicate with fbsplash via ioctls on the fbsplash
+device. These ioctls are to be used by both the userspace helper (called
+only by the kernel) and userspace configuration tools (run by the users).
+
+The splash helper should set the origin field to FB_SPLASH_IO_ORIG_KERNEL
+when doing the appropriate ioctls. All userspace configuration tools should
+use FB_SPLASH_IO_ORIG_USER. Failure to set the appropriate value in the origin
+field when performing ioctls from the kernel helper will most likely result
+in a console deadlock.
+
+FB_SPLASH_IO_ORIG_KERNEL instructs fbsplash not to try to acquire the console
+semaphore. Not surprisingly, FB_SPLASH_IO_ORIG_USER instructs it to acquire
+the console sem.
+
+The framebuffer splash provides the following ioctls (all defined in 
+linux/fb.h):
+
+FBIOSPLASH_SETPIC
+description: loads a background picture for a virtual console
+argument: struct fb_splash_iowrapper*; data: struct fb_image*
+notes: 
+If called for consoles other than the current foreground one, the picture data
+will be ignored.
+
+If the current virtual console is running in a 8-bpp mode, the cmap substruct
+of fb_image has to be filled appropriately: start should be set to 16 (first
+16 colors are reserved for fbcon), len to a value <= 240 and red, green and
+blue should point to valid cmap data. The transp field is ingored. The fields
+dx, dy, bg_color, fg_color in fb_image are ignored as well.
+
+FBIOSPLASH_SETCFG
+description: sets the fbsplash config for a virtual console
+argument: struct fb_splash_iowrapper*; data: struct vc_splash*
+notes: The structure has to be filled with valid data.
+
+FBIOSPLASH_GETCFG
+description: gets the fbsplash config for a virtual console
+argument: struct fb_splash_iowrapper*; data: struct vc_splash*
+
+FBIOSPLASH_SETSTATE
+description: sets the fbsplash state for a virtual console
+argument: struct fb_splash_iowrapper*; data: unsigned int*
+          values: 0 = disabled, 1 = enabled.
+
+FBIOSPLASH_GETSTATE
+description: gets the fbsplash state for a virtual console
+argument: struct fb_splash_iowrapper*; data: unsigned int*
+          values: as in FBIOSPLASH_SETSTATE
+
+Info on used structures:
+
+Definition of struct vc_splash can be found in linux/console_splash.h. It's
+heavily commented. Note that the 'theme' field should point to a string
+no longer than FB_SPLASH_THEME_LEN. When FBIOSPLASH_GETCFG call is
+performed, the theme field should point to a char buffer of length
+FB_SPLASH_THEME_LEN.
+
+Definition of struct fb_splash_iowrapper can be found in linux/fb.h.
+The fields in this struct have the following meaning:
+
+vc: 
+Virtual console number.
+
+origin: 
+Specifies if the ioctl is performed as a response to a kernel request. The
+splash helper should set this field to FB_SPLASH_IO_ORIG_KERNEL, userspace
+programs should set it to FB_SPLASH_IO_ORIG_USER. This field is necessary to
+avoid console semaphore deadlocks.
+
+data: 
+Pointer to a data structure appropriate for the performed ioctl. Type of
+the data struct is specified in the ioctls description.
+
+*****************************************************************************
+
+Credit
+------
+
+Original 'bootsplash' project & implementation by:
+  Volker Poplawski <volker@poplawski.de>, Stefan Reinauer <stepan@suse.de>,
+  Steffen Winterfeldt <snwint@suse.de>, Michael Schroeder <mls@suse.de>,
+  Ken Wimer <wimer@suse.de>.
+
+Fbsplash, splash protocol design, current implementation & docs by:
+  Michal Januszewski <spock@gentoo.org>
+
Index: linux-2.6.22/drivers/Makefile
===================================================================
--- linux-2.6.22.orig/drivers/Makefile
+++ linux-2.6.22/drivers/Makefile
@@ -8,6 +8,9 @@
 obj-$(CONFIG_PCI)		+= pci/
 obj-$(CONFIG_PARISC)		+= parisc/
 obj-$(CONFIG_RAPIDIO)		+= rapidio/
+# char/ comes before serial/ etc so that the VT console is the boot-time
+# default.
+obj-y				+= char/
 obj-y				+= video/
 obj-$(CONFIG_ACPI)		+= acpi/
 # PnP must come after ACPI since it will eventually need to check if acpi
@@ -15,10 +18,6 @@ obj-$(CONFIG_ACPI)		+= acpi/
 obj-$(CONFIG_PNP)		+= pnp/
 obj-$(CONFIG_ARM_AMBA)		+= amba/
 
-# char/ comes before serial/ etc so that the VT console is the boot-time
-# default.
-obj-y				+= char/
-
 obj-$(CONFIG_CONNECTOR)		+= connector/
 
 # i810fb and intelfb depend on char/agp/
Index: linux-2.6.22/drivers/video/Kconfig
===================================================================
--- linux-2.6.22.orig/drivers/video/Kconfig
+++ linux-2.6.22/drivers/video/Kconfig
@@ -586,8 +586,22 @@ config FB_TGA
 	  Say Y if you have one of those.
 
 config FB_VESA
-	bool "VESA VGA graphics support"
-	depends on (FB = y) && X86
+	tristate "VESA VGA graphics support"
+	depends on (FB = y) && (X86 || X86_64)
+	help
+	  This is the frame buffer device driver for generic VESA 2.0
+	  compliant graphic cards. The older VESA 1.2 cards are not supported.
+	  You will get a boot time penguin logo at no additional cost. Please
+	  read <file:Documentation/fb/vesafb.txt>. If unsure, say Y.
+
+choice 
+	prompt "VESA driver type"
+	depends on FB_VESA
+	default FB_VESA_STD if X86_64
+	default FB_VESA_TNG if X86
+
+config FB_VESA_STD
+	bool "vesafb"
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
@@ -596,7 +610,43 @@ config FB_VESA
 	  This is the frame buffer device driver for generic VESA 2.0
 	  compliant graphic cards. The older VESA 1.2 cards are not supported.
 	  You will get a boot time penguin logo at no additional cost. Please
-	  read <file:Documentation/fb/vesafb.txt>. If unsure, say Y.
+	  read <file:Documentation/fb/vesafb.txt>. Choose this driver if you
+	  are experiencing problems with vesafb-tng or if you own a 64-bit system.
+
+	  Note that this driver cannot be compiled as a module.
+
+config FB_VESA_TNG
+	bool "vesafb-tng"
+	depends on !X86_64
+	select FB_MODE_HELPERS
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	help
+	  This is an enhanced generic frame buffer device driver for
+	  VBE 2.0 compliant graphic cards. It can take advantage of VBE 3.0
+	  features (refresh rate adjustment) when these are available.
+	  The driver also makes it possible to change the video mode
+	  on the fly and to switch back to text mode when it's unloaded.
+
+	  If the driver is compiled as a module, the module will be called
+	  vesafb-tng.
+
+endchoice
+
+config FB_VESA_DEFAULT_MODE
+	string "VESA default mode"
+	depends on FB_VESA_TNG
+	default "640x480@60"
+	help 
+	  This option is used to determine the default mode vesafb is
+	  supposed to switch to in case no mode is provided as a kernel
+	  command line parameter.
+
+config VIDEO_SELECT
+	bool
+	depends on FB_VESA
+	default y
 
 config FB_IMAC
 	bool "Intel-based Macintosh Framebuffer Support"
@@ -1851,5 +1901,15 @@ if FB || SGI_NEWPORT_CONSOLE
 	source "drivers/video/logo/Kconfig"
 endif
 
-endmenu
+config FB_SPLASH
+	bool "Support for the framebuffer splash"
+	depends on FRAMEBUFFER_CONSOLE=y && !FB_TILEBLITTING
+	default n
+	---help---
+	  This option enables support for the Linux boot-up splash screen and
+	  graphical backgrounds on consoles. Note that you will need userspace
+	  splash utilities in order to take advantage of these features. Refer 
+	  to Documentation/fb/splash.txt for more information.
 
+	  If unsure, say N.
+endmenu
Index: linux-2.6.22/drivers/video/Makefile
===================================================================
--- linux-2.6.22.orig/drivers/video/Makefile
+++ linux-2.6.22/drivers/video/Makefile
@@ -14,6 +14,7 @@ fb-objs                           := $(f
 obj-$(CONFIG_VT)		  += console/
 obj-$(CONFIG_LOGO)		  += logo/
 obj-y				  += backlight/ display/
+obj-$(CONFIG_FB_SPLASH) 	   += fbsplash.o cfbsplash.o
 
 obj-$(CONFIG_FB_CFB_FILLRECT)  += cfbfillrect.o
 obj-$(CONFIG_FB_CFB_COPYAREA)  += cfbcopyarea.o
@@ -115,7 +116,11 @@ obj-$(CONFIG_FB_SM501)            += sm5
 obj-$(CONFIG_FB_XILINX)           += xilinxfb.o
 
 # Platform or fallback drivers go here
-obj-$(CONFIG_FB_VESA)             += vesafb.o
+ifeq ($(CONFIG_FB_VESA_STD),y)
+  obj-y				  += vesafb.o
+else
+  obj-$(CONFIG_FB_VESA)		  += vesafb-thread.o vesafb-tng.o
+endif
 obj-$(CONFIG_FB_IMAC)             += imacfb.o
 obj-$(CONFIG_FB_VGA16)            += vga16fb.o
 obj-$(CONFIG_FB_OF)               += offb.o
Index: linux-2.6.22/drivers/video/cfbsplash.c
===================================================================
--- /dev/null
+++ linux-2.6.22/drivers/video/cfbsplash.c
@@ -0,0 +1,471 @@
+/*
+ *  linux/drivers/video/cfbsplash.c -- Framebuffer splash render functions
+ *  
+ *  Copyright (C) 2004 Michal Januszewski <spock@gentoo.org>
+ *
+ *  Code based upon "Bootsplash" (C) 2001-2003 
+ *       Volker Poplawski <volker@poplawski.de>,
+ *       Stefan Reinauer <stepan@suse.de>,
+ *       Steffen Winterfeldt <snwint@suse.de>,
+ *       Michael Schroeder <mls@suse.de>,
+ *       Ken Wimer <wimer@suse.de>.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */ 
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/selection.h>
+#include <linux/vt_kern.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "console/fbcon.h"
+#include "fbsplash.h"
+
+#define parse_pixel(shift,bpp,type)						\
+	do {									\
+		if (d & (0x80 >> (shift)))					\
+			dd2[(shift)] = fgx;					\
+		else								\
+			dd2[(shift)] = transparent ? *(type *)splash_src : bgx;	\
+		splash_src += (bpp);						\
+	} while (0)								\
+
+extern int get_color(struct vc_data *vc, struct fb_info *info,
+		     u16 c, int is_fg);
+
+void fbsplash_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc)
+{
+	int i, j, k;
+	int minlen = min(min(info->var.red.length, info->var.green.length), 
+			     info->var.blue.length);
+	u32 col;
+	
+	for (j = i = 0; i < 16; i++) {
+		k = color_table[i];
+                      
+		col = ((vc->vc_palette[j++]  >> (8-minlen)) 
+			<< info->var.red.offset);
+		col |= ((vc->vc_palette[j++] >> (8-minlen)) 
+			<< info->var.green.offset);
+		col |= ((vc->vc_palette[j++] >> (8-minlen)) 
+			<< info->var.blue.offset);
+			((u32 *)info->pseudo_palette)[k] = col;
+	}
+}
+				
+void fbsplash_renderc(struct fb_info *info, int ypos, int xpos, int height, 
+		      int width, u8* src, u32 fgx, u32 bgx, u8 transparent)
+{	
+	unsigned int x, y;
+	u32 dd;
+	int bytespp = ((info->var.bits_per_pixel + 7) >> 3);
+	unsigned int d = ypos * info->fix.line_length + xpos * bytespp;
+	unsigned int ds = (ypos * info->var.xres + xpos) * bytespp;
+	u16 dd2[4];
+
+	u8* splash_src = (u8 *)(info->splash.data + ds);
+	u8* dst = (u8 *)(info->screen_base + d);
+
+	if ((ypos + height) > info->var.yres || (xpos + width) > info->var.xres)
+		return;
+	
+	for (y = 0; y < height; y++) {
+		switch (info->var.bits_per_pixel) {
+	
+		case 32:
+			for (x = 0; x < width; x++) {
+
+				if ((x & 7) == 0)
+					d = *src++;
+				if (d & 0x80)
+					dd = fgx;
+				else
+					dd = transparent ? 
+					     *(u32 *)splash_src : bgx;
+				
+				d <<= 1;
+				splash_src += 4;
+				fb_writel(dd, dst);
+				dst += 4;
+			}
+			break;
+		case 24:
+			for (x = 0; x < width; x++) {
+
+				if ((x & 7) == 0)
+					d = *src++;
+				if (d & 0x80)
+					dd = fgx;
+				else
+					dd = transparent ? 
+					     (*(u32 *)splash_src & 0xffffff) : bgx;
+				
+				d <<= 1;
+				splash_src += 3;
+#ifdef __LITTLE_ENDIAN
+				fb_writew(dd & 0xffff, dst);
+				dst += 2;
+				fb_writeb((dd >> 16), dst);
+#else
+				fb_writew(dd >> 8, dst);
+				dst += 2;
+				fb_writeb(dd & 0xff, dst);
+#endif
+				dst++;
+			}
+			break;
+		case 16:
+			for (x = 0; x < width; x += 2) {
+		    		if ((x & 7) == 0)
+					d = *src++;
+
+				parse_pixel(0, 2, u16);
+				parse_pixel(1, 2, u16);
+#ifdef __LITTLE_ENDIAN
+				dd = dd2[0] | (dd2[1] << 16);
+#else
+				dd = dd2[1] | (dd2[0] << 16);
+#endif
+				d <<= 2;
+				fb_writel(dd, dst);
+				dst += 4;
+			}
+			break;
+
+		case 8:
+			for (x = 0; x < width; x += 4) {
+				if ((x & 7) == 0)
+					d = *src++;
+	
+				parse_pixel(0, 1, u8);
+				parse_pixel(1, 1, u8);
+				parse_pixel(2, 1, u8);
+				parse_pixel(3, 1, u8);
+		
+#ifdef __LITTLE_ENDIAN
+				dd = dd2[0] | (dd2[1] << 8) | (dd2[2] << 16) | (dd2[3] << 24);
+#else
+				dd = dd2[3] | (dd2[2] << 8) | (dd2[1] << 16) | (dd2[0] << 24);
+#endif
+				d <<= 4;
+				fb_writel(dd, dst);
+				dst += 4;
+			}		
+		}
+
+		dst += info->fix.line_length - width * bytespp;
+		splash_src += (info->var.xres - width) * bytespp;
+    	}
+}
+
+#define cc2cx(a) 						\
+	((info->fix.visual == FB_VISUAL_TRUECOLOR || 		\
+	  info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? 		\
+	 ((u32*)info->pseudo_palette)[a] : a)
+
+void fbsplash_putcs(struct vc_data *vc, struct fb_info *info,
+		   const unsigned short *s, int count, int yy, int xx)
+{
+	unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+	struct fbcon_ops *ops = info->fbcon_par;
+	int fg_color, bg_color, transparent;
+	u8 *src;
+	u32 bgx, fgx;
+	u16 c = scr_readw(s);
+
+	fg_color = get_color(vc, info, c, 1);
+        bg_color = get_color(vc, info, c, 0);
+	
+	/* Don't paint the background image if console is blanked */
+	transparent = ops->blank_state ? 0 : 
+		(vc->vc_splash.bg_color == bg_color);
+
+	xx = xx * vc->vc_font.width + vc->vc_splash.tx;
+	yy = yy * vc->vc_font.height + vc->vc_splash.ty;
+
+	fgx = cc2cx(fg_color);
+	bgx = cc2cx(bg_color);
+
+	while (count--) {
+		c = scr_readw(s++);
+		src = vc->vc_font.data + (c & charmask) * vc->vc_font.height *
+		      ((vc->vc_font.width + 7) >> 3);
+
+		fbsplash_renderc(info, yy, xx, vc->vc_font.height, 
+			       vc->vc_font.width, src, fgx, bgx, transparent);
+		xx += vc->vc_font.width;
+	}
+}
+
+void fbsplash_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	int i;
+	unsigned int dsize, s_pitch;
+	struct fbcon_ops *ops = info->fbcon_par;
+	struct vc_data* vc;	
+	u8 *src;
+
+	/* we really don't need any cursors while the console is blanked */
+	if (info->state != FBINFO_STATE_RUNNING || ops->blank_state)
+		return;
+
+	vc = vc_cons[ops->currcon].d;
+
+	src = kmalloc(64 + sizeof(struct fb_image), GFP_ATOMIC);
+	if (!src)
+		return;
+
+	s_pitch = (cursor->image.width + 7) >> 3;
+	dsize = s_pitch * cursor->image.height;
+	if (cursor->enable) {	
+		switch (cursor->rop) {
+		case ROP_XOR:
+			for (i = 0; i < dsize; i++)
+				src[i] = cursor->image.data[i] ^ cursor->mask[i];
+                        break;
+		case ROP_COPY:
+		default:
+			for (i = 0; i < dsize; i++)
+				src[i] = cursor->image.data[i] & cursor->mask[i];
+			break;
+		}
+	} else
+		memcpy(src, cursor->image.data, dsize);
+
+	fbsplash_renderc(info,
+			cursor->image.dy + vc->vc_splash.ty,
+			cursor->image.dx + vc->vc_splash.tx,
+			cursor->image.height,
+			cursor->image.width,
+			(u8*)src,
+			cc2cx(cursor->image.fg_color),
+			cc2cx(cursor->image.bg_color),
+			cursor->image.bg_color == vc->vc_splash.bg_color);
+
+	kfree(src);
+}
+
+static void splashset(u8 *dst, int height, int width, int dstbytes, 
+		        u32 bgx, int bpp)
+{
+	int i;
+	
+	if (bpp == 8)
+		bgx |= bgx << 8;
+	if (bpp == 16 || bpp == 8)
+		bgx |= bgx << 16;
+	
+	while (height-- > 0) {
+		u8 *p = dst;
+		
+		switch (bpp) {
+		
+		case 32:
+			for (i=0; i < width; i++) {
+				fb_writel(bgx, p); p += 4;
+			}
+			break;
+		case 24:	
+			for (i=0; i < width; i++) {
+#ifdef __LITTLE_ENDIAN
+				fb_writew((bgx & 0xffff),(u16*)p); p += 2;
+				fb_writeb((bgx >> 16),p++);
+#else
+				fb_writew((bgx >> 8),(u16*)p); p += 2;
+				fb_writeb((bgx & 0xff),p++);
+#endif
+			}
+		case 16:
+			for (i=0; i < width/4; i++) {
+				fb_writel(bgx,p); p += 4;
+				fb_writel(bgx,p); p += 4;
+			}
+			if (width & 2) {
+				fb_writel(bgx,p); p += 4;
+			}
+			if (width & 1)
+				fb_writew(bgx,(u16*)p);
+			break;
+		case 8:
+			for (i=0; i < width/4; i++) {
+				fb_writel(bgx,p); p += 4;
+			}
+			
+			if (width & 2) {
+				fb_writew(bgx,p); p += 2;
+			}
+			if (width & 1)
+				fb_writeb(bgx,(u8*)p);
+			break;
+
+		}		
+		dst += dstbytes;
+	}
+}
+
+void fbsplash_copy(u8 *dst, u8 *src, int height, int width, int linebytes, 
+		   int srclinebytes, int bpp)
+{
+	int i;
+
+	while (height-- > 0) {
+		u32 *p = (u32 *)dst;
+		u32 *q = (u32 *)src;
+
+		switch (bpp) {
+	
+		case 32:
+			for (i=0; i < width; i++)
+				fb_writel(*q++, p++);
+			break;	
+		case 24:	
+			for (i=0; i < (width*3/4); i++)
+				fb_writel(*q++, p++);
+			if ((width*3) % 4) {
+				if (width & 2) {
+					fb_writeb(*(u8*)q, (u8*)p);
+				} else if (width & 1) {
+					fb_writew(*(u16*)q, (u16*)p);
+					fb_writeb(*(u8*)((u16*)q+1),(u8*)((u16*)p+2));
+				}
+			}
+			break;
+		case 16:
+			for (i=0; i < width/4; i++) {
+				fb_writel(*q++, p++);
+				fb_writel(*q++, p++);
+			}
+			if (width & 2)
+				fb_writel(*q++, p++);
+			if (width & 1)
+				fb_writew(*(u16*)q, (u16*)p);
+			break;
+		case 8:
+			for (i=0; i < width/4; i++) 
+				fb_writel(*q++, p++);
+				
+			if (width & 2) {
+				fb_writew(*(u16*)q, (u16*)p); 
+				q = (u32*) ((u16*)q + 1);
+				p = (u32*) ((u16*)p + 1);
+			}
+			if (width & 1)
+				fb_writeb(*(u8*)q, (u8*)p);
+			break;
+		}
+
+		dst += linebytes;
+		src += srclinebytes;
+	}
+}
+
+static void splashfill(struct fb_info *info, int sy, int sx, int height, 
+		       int width) 
+{
+	int bytespp = ((info->var.bits_per_pixel + 7) >> 3);
+	int d  = sy * info->fix.line_length + sx * bytespp;
+	int ds = (sy * info->var.xres + sx) * bytespp;
+
+	fbsplash_copy((u8 *)(info->screen_base + d), (u8 *)(info->splash.data + ds),
+		    height, width, info->fix.line_length, info->var.xres * bytespp,
+		    info->var.bits_per_pixel);
+}
+
+void fbsplash_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, 
+		    int height, int width)
+{
+	int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+	int bg_color = attr_bgcol_ec(bgshift, vc);
+	int transparent = vc->vc_splash.bg_color == bg_color;
+	struct fbcon_ops *ops = info->fbcon_par;
+	u8 *dst;
+
+	sy = sy * vc->vc_font.height + vc->vc_splash.ty;
+	sx = sx * vc->vc_font.width + vc->vc_splash.tx;
+	height *= vc->vc_font.height;
+	width *= vc->vc_font.width;
+
+	/* Don't paint the background image if console is blanked */
+	if (transparent && !ops->blank_state) {
+		splashfill(info, sy, sx, height, width);
+	} else {
+		dst = (u8 *)(info->screen_base + sy * info->fix.line_length + 
+			     sx * ((info->var.bits_per_pixel + 7) >> 3));
+		splashset(dst, height, width, info->fix.line_length, cc2cx(bg_color), 
+			  info->var.bits_per_pixel);
+	}
+}
+
+void fbsplash_clear_margins(struct vc_data *vc, struct fb_info *info, 
+			    int bottom_only)
+{
+	unsigned int tw = vc->vc_cols*vc->vc_font.width;
+	unsigned int th = vc->vc_rows*vc->vc_font.height;
+
+	if (!bottom_only) {
+		/* top margin */
+		splashfill(info, 0, 0, vc->vc_splash.ty, info->var.xres);
+		/* left margin */
+		splashfill(info, vc->vc_splash.ty, 0, th, vc->vc_splash.tx);
+		/* right margin */
+		splashfill(info, vc->vc_splash.ty, vc->vc_splash.tx + tw, th, 
+			   info->var.xres - vc->vc_splash.tx - tw);
+	}
+	splashfill(info, vc->vc_splash.ty + th, 0, 
+		   info->var.yres - vc->vc_splash.ty - th, info->var.xres);
+}
+
+void fbsplash_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, 
+			   int sx, int dx, int width)
+{
+	u16 *d = (u16 *) (vc->vc_origin + vc->vc_size_row * y + dx * 2);
+	u16 *s = d + (dx - sx);
+	u16 *start = d;
+	u16 *ls = d;
+	u16 *le = d + width;
+	u16 c;
+	int x = dx;
+	u16 attr = 1;
+
+	do {
+		c = scr_readw(d);
+		if (attr != (c & 0xff00)) {
+			attr = c & 0xff00;
+			if (d > start) {
+				fbsplash_putcs(vc, info, start, d - start, y, x);
+				x += d - start;
+				start = d;
+			}
+		}
+		if (s >= ls && s < le && c == scr_readw(s)) {
+			if (d > start) {
+				fbsplash_putcs(vc, info, start, d - start, y, x);
+				x += d - start + 1;
+				start = d + 1;
+			} else {
+				x++;
+				start++;
+			}
+		}
+		s++;
+		d++;
+	} while (d < le);
+	if (d > start)
+		fbsplash_putcs(vc, info, start, d - start, y, x);
+}
+
+void fbsplash_blank(struct vc_data *vc, struct fb_info *info, int blank)
+{
+	if (blank) {
+		splashset((u8 *)info->screen_base, info->var.yres, info->var.xres,
+			  info->fix.line_length, 0, info->var.bits_per_pixel);
+	} else {
+		update_screen(vc);
+		fbsplash_clear_margins(vc, info, 0);
+	}
+}
+
Index: linux-2.6.22/drivers/video/console/bitblit.c
===================================================================
--- linux-2.6.22.orig/drivers/video/console/bitblit.c
+++ linux-2.6.22/drivers/video/console/bitblit.c
@@ -17,6 +17,7 @@
 #include <linux/console.h>
 #include <asm/types.h>
 #include "fbcon.h"
+#include "../fbsplash.h"
 
 /*
  * Accelerated handlers.
@@ -54,6 +55,13 @@ static void bit_bmove(struct vc_data *vc
 	area.height = height * vc->vc_font.height;
 	area.width = width * vc->vc_font.width;
 
+	if (fbsplash_active(info, vc)) {
+ 		area.sx += vc->vc_splash.tx;
+ 		area.sy += vc->vc_splash.ty;
+ 		area.dx += vc->vc_splash.tx;
+ 		area.dy += vc->vc_splash.ty;
+ 	}
+
 	info->fbops->fb_copyarea(info, &area);
 }
 
@@ -379,11 +387,15 @@ static void bit_cursor(struct vc_data *v
 	cursor.image.depth = 1;
 	cursor.rop = ROP_XOR;
 
-	if (info->fbops->fb_cursor)
-		err = info->fbops->fb_cursor(info, &cursor);
+	if (fbsplash_active(info, vc)) {
+		fbsplash_cursor(info, &cursor);
+	} else {
+		if (info->fbops->fb_cursor)
+			err = info->fbops->fb_cursor(info, &cursor);
 
-	if (err)
-		soft_cursor(info, &cursor);
+		if (err)
+			soft_cursor(info, &cursor);
+	}
 
 	ops->cursor_reset = 0;
 }
Index: linux-2.6.22/drivers/video/console/fbcon.c
===================================================================
--- linux-2.6.22.orig/drivers/video/console/fbcon.c
+++ linux-2.6.22/drivers/video/console/fbcon.c
@@ -90,6 +90,7 @@
 #endif
 
 #include "fbcon.h"
+#include "../fbsplash.h"
 
 #ifdef FBCONDEBUG
 #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
@@ -105,7 +106,7 @@ enum {
 
 static struct display fb_display[MAX_NR_CONSOLES];
 
-static signed char con2fb_map[MAX_NR_CONSOLES];
+signed char con2fb_map[MAX_NR_CONSOLES];
 static signed char con2fb_map_boot[MAX_NR_CONSOLES];
 #ifndef MODULE
 static int logo_height;
@@ -302,7 +303,7 @@ static inline int fbcon_is_inactive(stru
 		vc->vc_mode != KD_TEXT || ops->graphics);
 }
 
-static inline int get_color(struct vc_data *vc, struct fb_info *info,
+inline int get_color(struct vc_data *vc, struct fb_info *info,
 	      u16 c, int is_fg)
 {
 	int depth = fb_get_color_depth(&info->var, &info->fix);
@@ -410,6 +411,7 @@ static void fb_flashcursor(struct work_s
 		CM_ERASE : CM_DRAW;
 	ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1),
 		    get_color(vc, info, c, 0));
+	
 	release_console_sem();
 }
 
@@ -575,6 +577,8 @@ static int fbcon_takeover(int show_logo)
 		info_idx = -1;
 	}
 
+	fbsplash_init();
+
 	return err;
 }
 
@@ -1018,6 +1022,12 @@ static const char *fbcon_startup(void)
 	rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
 	cols /= vc->vc_font.width;
 	rows /= vc->vc_font.height;
+
+	if (fbsplash_active(info, vc)) {
+		cols = vc->vc_splash.twidth / vc->vc_font.width;
+		rows = vc->vc_splash.theight / vc->vc_font.height;
+	}
+
 	vc_resize(vc, cols, rows);
 
 	DPRINTK("mode:   %s\n", info->fix.id);
@@ -1101,7 +1111,7 @@ static void fbcon_init(struct vc_data *v
 	cap = info->flags;
 
 	if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
-	    (info->fix.type == FB_TYPE_TEXT))
+	    (info->fix.type == FB_TYPE_TEXT) || fbsplash_active(info, vc))
 		logo = 0;
 
 	if (var_to_display(p, &info->var, info))
@@ -1297,6 +1307,11 @@ static void fbcon_clear(struct vc_data *
 	if (!height || !width)
 		return;
 
+ 	if (fbsplash_active(info, vc)) {
+ 		fbsplash_clear(vc, info, sy, sx, height, width);
+ 		return;
+ 	}
+ 	
 	/* Split blits that cross physical y_wrap boundary */
 
 	y_break = p->vrows - p->yscroll;
@@ -1316,10 +1331,15 @@ static void fbcon_putcs(struct vc_data *
 	struct display *p = &fb_display[vc->vc_num];
 	struct fbcon_ops *ops = info->fbcon_par;
 
-	if (!fbcon_is_inactive(vc, info))
-		ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
-			   get_color(vc, info, scr_readw(s), 1),
-			   get_color(vc, info, scr_readw(s), 0));
+	if (!fbcon_is_inactive(vc, info)) {
+		
+		if (fbsplash_active(info, vc))
+			fbsplash_putcs(vc, info, s, count, ypos, xpos);
+		else
+			ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
+				   get_color(vc, info, scr_readw(s), 1),
+				   get_color(vc, info, scr_readw(s), 0));
+	}
 }
 
 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
@@ -1335,8 +1355,13 @@ static void fbcon_clear_margins(struct v
 	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
 	struct fbcon_ops *ops = info->fbcon_par;
 
-	if (!fbcon_is_inactive(vc, info))
-		ops->clear_margins(vc, info, bottom_only);
+	if (!fbcon_is_inactive(vc, info)) {
+	 	if (fbsplash_active(info, vc)) {
+	 		fbsplash_clear_margins(vc, info, bottom_only);
+ 		} else {
+			ops->clear_margins(vc, info, bottom_only);
+		}
+	}
 }
 
 static void fbcon_cursor(struct vc_data *vc, int mode)
@@ -1809,7 +1834,7 @@ static int fbcon_scroll(struct vc_data *
 			count = vc->vc_rows;
 		if (softback_top)
 			fbcon_softback_note(vc, t, count);
-		if (logo_shown >= 0)
+		if (logo_shown >= 0 || fbsplash_active(info, vc))
 			goto redraw_up;
 		switch (p->scrollmode) {
 		case SCROLL_MOVE:
@@ -1897,6 +1922,8 @@ static int fbcon_scroll(struct vc_data *
 			count = vc->vc_rows;
 		if (logo_shown >= 0)
 			goto redraw_down;
+		if (fbsplash_active(info, vc))
+			goto redraw_down;
 		switch (p->scrollmode) {
 		case SCROLL_MOVE:
 			ops->bmove(vc, info, t, 0, t + count, 0,
@@ -2039,6 +2066,13 @@ static void fbcon_bmove_rec(struct vc_da
 		}
 		return;
 	}
+
+	if (fbsplash_active(info, vc) && sy == dy && height == 1) {
+ 		/* must use slower redraw bmove to keep background pic intact */
+ 		fbsplash_bmove_redraw(vc, info, sy, sx, dx, width);
+ 		return;
+ 	}
+	
 	ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
 		   height, width);
 }
@@ -2109,8 +2143,9 @@ static int fbcon_resize(struct vc_data *
 	var.yres = virt_h * virt_fh;
 	x_diff = info->var.xres - var.xres;
 	y_diff = info->var.yres - var.yres;
-	if (x_diff < 0 || x_diff > virt_fw ||
-	    y_diff < 0 || y_diff > virt_fh) {
+ 
+ 	if ((x_diff < 0 || x_diff > virt_fw ||
+ 	    y_diff < 0 || y_diff > virt_fh) && !vc->vc_splash.state) {
 		const struct fb_videomode *mode;
 
 		DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
@@ -2146,6 +2181,25 @@ static int fbcon_switch(struct vc_data *
 
 	info = registered_fb[con2fb_map[vc->vc_num]];
 	ops = info->fbcon_par;
+	prev_console = ops->currcon;
+	if (prev_console != -1)
+		old_info = registered_fb[con2fb_map[prev_console]];
+
+	if (fbsplash_active_vc(vc)) {
+		struct vc_data *vc_curr = vc_cons[prev_console].d;
+		if (!vc_curr->vc_splash.theme || strcmp(vc->vc_splash.theme, vc_curr->vc_splash.theme)) {
+			if (fbsplash_call_helper("getpic", vc->vc_num))
+				fbsplash_disable(vc, 0);
+		}
+	} else if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { 
+		struct vc_data *vc_curr = vc_cons[prev_console].d;
+		if (vc_curr && fbsplash_active_vc(vc_curr)) {
+			/* Clear the screen to avoid displaying funky colors during
+			 * palette updates. */ 
+			memset((u8*)info->screen_base + info->fix.line_length * info->var.yoffset,
+			       0, info->var.yres * info->fix.line_length);
+		}
+	}
 
 	if (softback_top) {
 		if (softback_lines)
@@ -2164,9 +2218,6 @@ static int fbcon_switch(struct vc_data *
 		logo_shown = FBCON_LOGO_CANSHOW;
 	}
 
-	prev_console = ops->currcon;
-	if (prev_console != -1)
-		old_info = registered_fb[con2fb_map[prev_console]];
 	/*
 	 * FIXME: If we have multiple fbdev's loaded, we need to
 	 * update all info->currcon.  Perhaps, we can place this
@@ -2205,6 +2256,11 @@ static int fbcon_switch(struct vc_data *
 		if (old_info != info)
 			fbcon_del_cursor_timer(old_info);
 	}
+	
+	if (fbsplash_active_nores(info, vc) && !fbsplash_active(info, vc)) {
+		if (fbsplash_call_helper("modechange", vc->vc_num))
+			fbsplash_disable(vc, 0);
+	}
 
 	if (fbcon_is_inactive(vc, info) ||
 	    ops->blank_state != FB_BLANK_UNBLANK)
@@ -2320,8 +2376,12 @@ static int fbcon_blank(struct vc_data *v
 			fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
 			ops->cursor_flash = (!blank);
 
-			if (fb_blank(info, blank))
-				fbcon_generic_blank(vc, info, blank);
+			if (fb_blank(info, blank)) {
+				if (fbsplash_active(info, vc))
+					fbsplash_blank(vc, info, blank);
+				else 
+					fbcon_generic_blank(vc, info, blank);
+			}
 		}
 
 		if (!blank)
@@ -2472,13 +2532,22 @@ static int fbcon_do_set_font(struct vc_d
 	}
 
 	if (resize) {
+		/* reset wrap/pan */
 		int cols, rows;
 
 		cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
+
+ 		info->var.xoffset = info->var.yoffset = p->yscroll = 0;
+		if (fbsplash_active(info, vc)) {
+			cols = vc->vc_splash.twidth;
+			rows = vc->vc_splash.theight;
+		}
 		cols /= w;
 		rows /= h;
+
 		vc_resize(vc, cols, rows);
+
 		if (CON_IS_VISIBLE(vc) && softback_buf)
 			fbcon_update_softback(vc);
 	} else if (CON_IS_VISIBLE(vc)
@@ -2607,7 +2676,7 @@ static int fbcon_set_palette(struct vc_d
 	int i, j, k, depth;
 	u8 val;
 
-	if (fbcon_is_inactive(vc, info))
+	if (fbcon_is_inactive(vc, info) || vc->vc_num != fg_console)
 		return -EINVAL;
 
 	if (!CON_IS_VISIBLE(vc))
@@ -2633,7 +2702,49 @@ static int fbcon_set_palette(struct vc_d
 	} else
 		fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
 
-	return fb_set_cmap(&palette_cmap, info);
+	if (fbsplash_active(info, vc_cons[fg_console].d) &&
+	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+
+		u16 *red, *green, *blue;
+		int minlen = min(min(info->var.red.length, info->var.green.length), 
+				     info->var.blue.length);
+		int h;
+
+		struct fb_cmap cmap = {
+			.start = 0,
+			.len = (1 << minlen),
+			.red = NULL,
+			.green = NULL,
+			.blue = NULL,
+			.transp = NULL
+		};
+
+		red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL);
+	
+		if (!red)
+			goto out;		
+	
+		green = red + 256;
+		blue = green + 256;
+		cmap.red = red;
+		cmap.green = green;
+		cmap.blue = blue;
+		
+		for (i = 0; i < cmap.len; i++) {
+			red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1);
+		}
+
+		h = fb_set_cmap(&cmap, info);
+		fbsplash_fix_pseudo_pal(info, vc_cons[fg_console].d);
+		kfree(red);
+		
+		return h;
+		
+	} else if (fbsplash_active(info, vc_cons[fg_console].d) && 
+		   info->var.bits_per_pixel == 8 && info->splash.cmap.red != NULL) 
+		fb_set_cmap(&info->splash.cmap, info);
+		
+out:	return fb_set_cmap(&palette_cmap, info);
 }
 
 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
@@ -2859,7 +2970,14 @@ static void fbcon_modechanged(struct fb_
 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
 		cols /= vc->vc_font.width;
 		rows /= vc->vc_font.height;
-		vc_resize(vc, cols, rows);
+				
+		if (!fbsplash_active_nores(info, vc)) {
+			vc_resize(vc, cols, rows);
+		} else {
+			if (fbsplash_call_helper("modechange", vc->vc_num))
+				fbsplash_disable(vc, 0);
+		}
+
 		updatescrollmode(p, info, vc);
 		scrollback_max = 0;
 		scrollback_current = 0;
@@ -3347,6 +3465,7 @@ static void fbcon_exit(void)
 		}
 	}
 
+	fbsplash_exit();
 	fbcon_has_exited = 1;
 }
 
Index: linux-2.6.22/drivers/video/fbcmap.c
===================================================================
--- linux-2.6.22.orig/drivers/video/fbcmap.c
+++ linux-2.6.22/drivers/video/fbcmap.c
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/fb.h>
 #include <linux/slab.h>
+#include "fbsplash.h"
 
 #include <asm/uaccess.h>
 
@@ -235,14 +236,17 @@ int fb_set_cmap(struct fb_cmap *cmap, st
 			if (transp)
 				htransp = *transp++;
 			if (info->fbops->fb_setcolreg(start++,
-						      hred, hgreen, hblue,
+						      hred, hgreen, hblue, 
 						      htransp, info))
 				break;
 		}
 	}
-	if (rc == 0)
+	if (rc == 0) {
 		fb_copy_cmap(cmap, &info->cmap);
-
+		if (fbsplash_active(info, vc_cons[fg_console].d) &&
+		    info->fix.visual == FB_VISUAL_DIRECTCOLOR)
+			fbsplash_fix_pseudo_pal(info, vc_cons[fg_console].d);
+	}
 	return rc;
 }
 
@@ -250,7 +254,7 @@ int fb_set_user_cmap(struct fb_cmap_user
 {
 	int rc, size = cmap->len * sizeof(u16);
 	struct fb_cmap umap;
-
+	
 	if (cmap->start < 0 || (!info->fbops->fb_setcolreg &&
 			        !info->fbops->fb_setcmap))
 		return -EINVAL;
Index: linux-2.6.22/drivers/video/fbsplash.c
===================================================================
--- /dev/null
+++ linux-2.6.22/drivers/video/fbsplash.c
@@ -0,0 +1,424 @@
+/* 
+ *  linux/drivers/video/fbsplash.c -- Framebuffer splash routines
+ *
+ *  Copyright (C) 2004 Michal Januszewski <spock@gentoo.org>
+ *
+ *  Code based upon "Bootsplash" (C) 2001-2003 
+ *       Volker Poplawski <volker@poplawski.de>,
+ *       Stefan Reinauer <stepan@suse.de>,
+ *       Steffen Winterfeldt <snwint@suse.de>,
+ *       Michael Schroeder <mls@suse.de>,
+ *       Ken Wimer <wimer@suse.de>.
+ *
+ *  Splash render routines are located in /linux/drivers/video/cfbsplash.c
+ * 
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ * 
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/vmalloc.h>
+#include <linux/unistd.h>
+#include <linux/syscalls.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/workqueue.h>
+#include <linux/kmod.h>
+#include <linux/miscdevice.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "console/fbcon.h"
+#include "fbsplash.h"
+
+#define SPLASH_VERSION 		"0.9.2"
+
+extern signed char con2fb_map[];
+static int fbsplash_enable(struct vc_data *vc);
+char fbsplash_path[KMOD_PATH_LEN] = "/sbin/splash_helper";
+static int initialized = 0;
+
+int fbsplash_call_helper(char* cmd, unsigned short vc)
+{
+	char *envp[] = {
+		"HOME=/",
+		"PATH=/sbin:/bin",
+		NULL
+	};
+
+	char tfb[5];
+	char tcons[5];
+	unsigned char fb = (int) con2fb_map[vc];
+
+	char *argv[] = {
+		fbsplash_path,
+		"2",
+		cmd,
+		tcons,
+		tfb,
+		vc_cons[vc].d->vc_splash.theme,
+		NULL
+	};
+
+	snprintf(tfb,5,"%d",fb);
+	snprintf(tcons,5,"%d",vc);
+
+	return call_usermodehelper(fbsplash_path, argv, envp, 1);
+}
+
+/* Disables fbsplash on a virtual console; called with console sem held. */
+int fbsplash_disable(struct vc_data *vc, unsigned char redraw)
+{
+	struct fb_info* info;
+
+	if (!vc->vc_splash.state)
+		return -EINVAL;
+
+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
+
+	if (info == NULL)
+		return -EINVAL;
+
+	vc->vc_splash.state = 0; 
+	vc_resize(vc, info->var.xres / vc->vc_font.width, 
+		  info->var.yres / vc->vc_font.height);
+
+	if (fg_console == vc->vc_num && redraw) {
+		redraw_screen(vc, 0);
+		update_region(vc, vc->vc_origin + 
+			      vc->vc_size_row * vc->vc_top, 
+			      vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2);
+	}
+
+	printk(KERN_INFO "fbsplash: switched splash state to 'off' on console %d\n", 
+			 vc->vc_num);
+
+	return 0;
+}
+
+/* Enables fbsplash on a virtual console; called with console sem held. */
+static int fbsplash_enable(struct vc_data *vc)
+{
+	struct fb_info* info;
+
+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
+		
+	if (vc->vc_splash.twidth == 0 || vc->vc_splash.theight == 0 || 
+	    info == NULL || vc->vc_splash.state || (!info->splash.data &&
+	    vc->vc_num == fg_console))
+		return -EINVAL;
+	
+	vc->vc_splash.state = 1;
+	vc_resize(vc, vc->vc_splash.twidth / vc->vc_font.width, 
+		  vc->vc_splash.theight / vc->vc_font.height);
+
+	if (fg_console == vc->vc_num) {
+		redraw_screen(vc, 0);
+		update_region(vc, vc->vc_origin + 
+			      vc->vc_size_row * vc->vc_top, 
+			      vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2);
+		fbsplash_clear_margins(vc, info, 0);
+	}
+
+	printk(KERN_INFO "fbsplash: switched splash state to 'on' on console %d\n", 
+			 vc->vc_num);
+
+	return 0;
+}
+
+static inline int fbsplash_ioctl_dosetstate(struct vc_data *vc, unsigned int __user* state, unsigned char origin)
+{
+	int tmp, ret;
+
+	if (get_user(tmp, state))
+		return -EFAULT;
+
+	if (origin == FB_SPLASH_IO_ORIG_USER)
+		acquire_console_sem();
+	if (!tmp)
+		ret = fbsplash_disable(vc, 1);
+	else
+		ret = fbsplash_enable(vc);
+	if (origin == FB_SPLASH_IO_ORIG_USER)
+		release_console_sem();
+
+	return ret;
+}
+
+static inline int fbsplash_ioctl_dogetstate(struct vc_data *vc, unsigned int __user *state)
+{
+	return put_user(vc->vc_splash.state, (unsigned int __user*) state);
+}
+
+static int fbsplash_ioctl_dosetcfg(struct vc_data *vc, struct vc_splash __user *arg, unsigned char origin)
+{
+	struct vc_splash cfg;
+	struct fb_info *info;
+	int len;
+	char *tmp;
+	
+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
+
+	if (copy_from_user(&cfg, arg, sizeof(struct vc_splash)))
+		return -EFAULT;
+	if (info == NULL || !cfg.twidth || !cfg.theight || 
+	    cfg.tx + cfg.twidth  > info->var.xres ||
+	    cfg.ty + cfg.theight > info->var.yres)
+		return -EINVAL;
+
+	len = strlen_user(cfg.theme);
+	if (!len || len > FB_SPLASH_THEME_LEN)
+		return -EINVAL;
+	tmp = kmalloc(len, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+	if (copy_from_user(tmp, (void __user *)cfg.theme, len))
+		return -EFAULT;
+	cfg.theme = tmp;
+	cfg.state = 0;
+
+	/* If this ioctl is a response to a request from kernel, the console sem
+	 * is already held; we also don't need to disable splash because either the
+	 * new config and background picture will be successfully loaded, and the 
+	 * splash will stay on, or in case of a failure it'll be turned off in fbcon. */
+	if (origin == FB_SPLASH_IO_ORIG_USER) {
+		acquire_console_sem();
+		if (vc->vc_splash.state)
+			fbsplash_disable(vc, 1);
+	}
+
+	if (vc->vc_splash.theme)
+		kfree(vc->vc_splash.theme);
+
+	vc->vc_splash = cfg;
+
+	if (origin == FB_SPLASH_IO_ORIG_USER)
+		release_console_sem();
+
+	printk(KERN_INFO "fbsplash: console %d using theme '%s'\n", 
+			 vc->vc_num, vc->vc_splash.theme);
+	return 0;	
+}
+
+static int fbsplash_ioctl_dogetcfg(struct vc_data *vc, struct vc_splash __user *arg)
+{
+	struct vc_splash splash;
+	char __user *tmp;
+
+	if (get_user(tmp, &arg->theme))
+		return -EFAULT;
+	
+	splash = vc->vc_splash;
+	splash.theme = tmp;
+
+	if (vc->vc_splash.theme) {
+		if (copy_to_user(tmp, vc->vc_splash.theme, strlen(vc->vc_splash.theme) + 1))
+			return -EFAULT;
+	} else
+		if (put_user(0, tmp))
+			return -EFAULT;
+
+	if (copy_to_user(arg, &splash, sizeof(struct vc_splash)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int fbsplash_ioctl_dosetpic(struct vc_data *vc, struct fb_image __user *arg, unsigned char origin)
+{
+	struct fb_image img;
+	struct fb_info *info;
+	int len;
+	u8 *tmp;
+	
+	if (vc->vc_num != fg_console) 
+		return -EINVAL;
+
+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
+	
+	if (info == NULL)
+		return -EINVAL;
+	
+	if (copy_from_user(&img, arg, sizeof(struct fb_image)))
+		return -EFAULT;
+	
+	if (img.width != info->var.xres || img.height != info->var.yres) {
+		printk(KERN_ERR "fbsplash: picture dimensions mismatch\n");
+		return -EINVAL;
+	}
+
+	if (img.depth != info->var.bits_per_pixel) {
+		printk(KERN_ERR "fbsplash: picture depth mismatch\n");
+		return -EINVAL;
+	}
+		
+	if (img.depth == 8) {
+		if (!img.cmap.len || !img.cmap.red || !img.cmap.green || 
+		    !img.cmap.blue)
+			return -EINVAL;
+		
+		tmp = vmalloc(img.cmap.len * 3 * 2);
+		if (!tmp)
+			return -ENOMEM;
+
+		if (copy_from_user(tmp, (void __user*)img.cmap.red, img.cmap.len * 2) ||
+		    copy_from_user(tmp + (img.cmap.len << 1),
+			    	   (void __user*)img.cmap.green, (img.cmap.len << 1)) ||
+		    copy_from_user(tmp + (img.cmap.len << 2),
+			    	   (void __user*)img.cmap.blue, (img.cmap.len << 1))) {
+			vfree(tmp);
+			return -EFAULT;
+		}
+			
+		img.cmap.transp = NULL;
+		img.cmap.red = (u16*)tmp;
+		img.cmap.green = img.cmap.red + img.cmap.len;
+		img.cmap.blue = img.cmap.green + img.cmap.len;
+	} else {
+		img.cmap.red = NULL;
+	}
+		
+	len = ((img.depth + 7) >> 3) * img.width * img.height;
+	tmp = vmalloc(len);
+
+	if (!tmp)
+		goto out;
+
+	if (copy_from_user(tmp, (void __user*)img.data, len))
+		goto out;
+		
+	img.data = tmp;
+
+	/* If this ioctl is a response to a request from kernel, the console sem
+	 * is already held. */
+	if (origin == FB_SPLASH_IO_ORIG_USER)
+		acquire_console_sem();
+	
+	if (info->splash.data)
+		vfree((u8*)info->splash.data);
+	if (info->splash.cmap.red)
+		vfree(info->splash.cmap.red);
+	
+	info->splash = img;
+
+	if (origin == FB_SPLASH_IO_ORIG_USER)
+		release_console_sem();
+
+	return 0;
+
+out:	if (img.cmap.red)
+		vfree(img.cmap.red);
+	if (tmp)
+		vfree(tmp);
+	return -ENOMEM;
+}
+
+static int splash_ioctl(struct inode * inode, struct file *filp, u_int cmd, 
+			u_long arg)
+{
+	struct fb_splash_iowrapper __user *wrapper = (void __user*) arg;
+	struct vc_data *vc = NULL;
+	unsigned short vc_num = 0;
+	unsigned char origin = 0;
+	void __user *data = NULL;
+	
+	if (!access_ok(VERIFY_READ, wrapper, 
+			sizeof(struct fb_splash_iowrapper)))
+		return -EFAULT;
+	
+	__get_user(vc_num, &wrapper->vc);
+	__get_user(origin, &wrapper->origin);
+	__get_user(data, &wrapper->data);
+		
+	if (!vc_cons_allocated(vc_num))
+		return -EINVAL;
+
+	vc = vc_cons[vc_num].d;
+	
+	switch (cmd) {
+	case FBIOSPLASH_SETPIC:
+		return fbsplash_ioctl_dosetpic(vc, (struct fb_image __user*)data, origin);
+	case FBIOSPLASH_SETCFG:
+		return fbsplash_ioctl_dosetcfg(vc, (struct vc_splash*)data, origin);
+	case FBIOSPLASH_GETCFG:
+		return fbsplash_ioctl_dogetcfg(vc, (struct vc_splash*)data);
+	case FBIOSPLASH_SETSTATE:
+		return fbsplash_ioctl_dosetstate(vc, (unsigned int *)data, origin);
+	case FBIOSPLASH_GETSTATE:
+		return fbsplash_ioctl_dogetstate(vc, (unsigned int *)data);
+	default:
+		return -ENOIOCTLCMD;
+	}	
+}
+
+static struct file_operations splash_ops = {
+	.owner = THIS_MODULE,
+	.ioctl = splash_ioctl
+};
+
+static struct miscdevice splash_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "fbsplash",
+	.fops = &splash_ops
+};
+
+void fbsplash_reset(void)
+{
+	struct fb_info *info;
+	struct vc_data *vc;
+	int i;
+
+	vc = vc_cons[0].d;
+	info = registered_fb[0];
+
+	for (i = 0; i < num_registered_fb; i++) {
+		registered_fb[i]->splash.data = NULL;
+		registered_fb[i]->splash.cmap.red = NULL;
+	}
+
+	for (i = 0; i < MAX_NR_CONSOLES && vc_cons[i].d; i++) {
+		vc_cons[i].d->vc_splash.state = vc_cons[i].d->vc_splash.twidth = 
+						vc_cons[i].d->vc_splash.theight = 0;
+		vc_cons[i].d->vc_splash.theme = NULL;
+	}
+
+	return;
+}
+
+int fbsplash_init(void)
+{
+	int i;
+
+	fbsplash_reset();
+
+	if (initialized)
+		return 0;
+
+	i = misc_register(&splash_dev);
+	if (i) {
+		printk(KERN_ERR "fbsplash: failed to register device\n");
+		return i;
+	}
+
+	fbsplash_call_helper("init", 0);
+	initialized = 1;
+	return 0;
+}
+
+int fbsplash_exit(void)
+{
+	fbsplash_reset();
+	return 0;
+}
+
+EXPORT_SYMBOL(fbsplash_path);
Index: linux-2.6.22/drivers/video/fbsplash.h
===================================================================
--- /dev/null
+++ linux-2.6.22/drivers/video/fbsplash.h
@@ -0,0 +1,78 @@
+/* 
+ *  linux/drivers/video/fbsplash.h -- Framebuffer splash headers
+ *
+ *  Copyright (C) 2004 Michal Januszewski <spock@gentoo.org>
+ *
+ */
+
+#ifndef __FB_SPLASH_H
+#define __FB_SPLASH_H
+
+#ifndef _LINUX_FB_H
+#include <linux/fb.h>
+#endif
+
+/* This is needed for vc_cons in fbcmap.c */
+#include <linux/vt_kern.h>
+
+struct fb_cursor;
+struct fb_info;
+struct vc_data;
+
+#ifdef CONFIG_FB_SPLASH
+/* fbsplash.c */
+int fbsplash_init(void);
+int fbsplash_exit(void);
+int fbsplash_call_helper(char* cmd, unsigned short cons);
+int fbsplash_disable(struct vc_data *vc, unsigned char redraw);
+
+/* cfbsplash.c */
+void fbsplash_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx);
+void fbsplash_cursor(struct fb_info *info, struct fb_cursor *cursor);
+void fbsplash_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width);
+void fbsplash_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only);
+void fbsplash_blank(struct vc_data *vc, struct fb_info *info, int blank);
+void fbsplash_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width);
+void fbsplash_copy(u8 *dst, u8 *src, int height, int width, int linebytes, int srclinesbytes, int bpp);
+void fbsplash_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc);
+
+/* vt.c */
+void acquire_console_sem(void);
+void release_console_sem(void);
+void do_unblank_screen(int entering_gfx);
+
+/* struct vc_data *y */
+#define fbsplash_active_vc(y) (y->vc_splash.state && y->vc_splash.theme) 
+
+/* struct fb_info *x, struct vc_data *y */
+#define fbsplash_active_nores(x,y) (x->splash.data && fbsplash_active_vc(y))
+
+/* struct fb_info *x, struct vc_data *y */
+#define fbsplash_active(x,y) (fbsplash_active_nores(x,y) &&		\
+			      x->splash.width == x->var.xres && 	\
+			      x->splash.height == x->var.yres &&	\
+			      x->splash.depth == x->var.bits_per_pixel)
+
+
+#else /* CONFIG_FB_SPLASH */
+
+static inline void fbsplash_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx) {}
+static inline void fbsplash_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos) {}
+static inline void fbsplash_cursor(struct fb_info *info, struct fb_cursor *cursor) {}
+static inline void fbsplash_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) {}
+static inline void fbsplash_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only) {}
+static inline void fbsplash_blank(struct vc_data *vc, struct fb_info *info, int blank) {}
+static inline void fbsplash_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) {}
+static inline void fbsplash_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) {}
+static inline int fbsplash_call_helper(char* cmd, unsigned short cons) { return 0; }
+static inline int fbsplash_init(void) { return 0; }
+static inline int fbsplash_exit(void) { return 0; }
+static inline int fbsplash_disable(struct vc_data *vc, unsigned char redraw) { return 0; }
+
+#define fbsplash_active_vc(y) (0)
+#define fbsplash_active_nores(x,y) (0)
+#define fbsplash_active(x,y) (0)
+
+#endif /* CONFIG_FB_SPLASH */
+
+#endif /* __FB_SPLASH_H */
Index: linux-2.6.22/include/linux/console_splash.h
===================================================================
--- /dev/null
+++ linux-2.6.22/include/linux/console_splash.h
@@ -0,0 +1,13 @@
+#ifndef _LINUX_CONSOLE_SPLASH_H_
+#define _LINUX_CONSOLE_SPLASH_H_ 1
+
+/* A structure used by the framebuffer splash code (drivers/video/fbsplash.c) */
+struct vc_splash {
+	__u8 bg_color;				/* The color that is to be treated as transparent */
+	__u8 state;				/* Current splash state: 0 = off, 1 = on */
+	__u16 tx, ty;				/* Top left corner coordinates of the text field */
+	__u16 twidth, theight;			/* Width and height of the text field */
+	char* theme;
+};
+
+#endif
Index: linux-2.6.22/include/linux/console_struct.h
===================================================================
--- linux-2.6.22.orig/include/linux/console_struct.h
+++ linux-2.6.22/include/linux/console_struct.h
@@ -19,6 +19,7 @@
 struct vt_struct;
 
 #define NPAR 16
+#include <linux/console_splash.h>
 
 struct vc_data {
 	unsigned short	vc_num;			/* Console number */
@@ -105,6 +106,8 @@ struct vc_data {
 	struct vc_data **vc_display_fg;		/* [!] Ptr to var holding fg console for this display */
 	unsigned long	vc_uni_pagedir;
 	unsigned long	*vc_uni_pagedir_loc;  /* [!] Location of uni_pagedir variable for this console */
+
+	struct vc_splash vc_splash;
 	/* additional information is in vt_kern.h */
 };
 
Index: linux-2.6.22/include/linux/fb.h
===================================================================
--- linux-2.6.22.orig/include/linux/fb.h
+++ linux-2.6.22/include/linux/fb.h
@@ -11,6 +11,13 @@ struct dentry;
 #define FB_MAJOR		29
 #define FB_MAX			32	/* sufficient for now */
 
+struct fb_splash_iowrapper
+{
+	unsigned short vc;		/* Virtual console */
+	unsigned char origin;		/* Point of origin of the request */
+	void *data;
+};
+
 /* ioctls
    0x46 is 'F'								*/
 #define FBIOGET_VSCREENINFO	0x4600
@@ -38,7 +45,15 @@ struct dentry;
 #define FBIOGET_HWCINFO         0x4616
 #define FBIOPUT_MODEINFO        0x4617
 #define FBIOGET_DISPINFO        0x4618
-
+#define FBIOSPLASH_SETCFG	_IOWR('F', 0x19, struct fb_splash_iowrapper)
+#define FBIOSPLASH_GETCFG	_IOR('F', 0x1A, struct fb_splash_iowrapper)
+#define FBIOSPLASH_SETSTATE	_IOWR('F', 0x1B, struct fb_splash_iowrapper)
+#define FBIOSPLASH_GETSTATE	_IOR('F', 0x1C, struct fb_splash_iowrapper)
+#define FBIOSPLASH_SETPIC 	_IOWR('F', 0x1D, struct fb_splash_iowrapper)
+
+#define FB_SPLASH_THEME_LEN		128	/* Maximum lenght of a theme name */
+#define FB_SPLASH_IO_ORIG_KERNEL	0	/* Kernel ioctl origin */
+#define FB_SPLASH_IO_ORIG_USER		1 	/* User ioctl origin */
 
 #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
 #define FB_TYPE_PLANES			1	/* Non interleaved planes */
@@ -828,6 +843,9 @@ struct fb_info {
 #define FBINFO_STATE_SUSPENDED	1
 	u32 state;			/* Hardware state i.e suspend */
 	void *fbcon_par;                /* fbcon use-only private area */
+
+	struct fb_image splash;
+
 	/* From here on everything is device dependent */
 	void *par;	
 };
Index: linux-2.6.22/include/linux/sysctl.h
===================================================================
--- linux-2.6.22.orig/include/linux/sysctl.h
+++ linux-2.6.22/include/linux/sysctl.h
@@ -165,6 +165,10 @@ enum
 	KERN_MAX_LOCK_DEPTH=74,
 	KERN_NMI_WATCHDOG=75, /* int: enable/disable nmi watchdog */
 	KERN_PANIC_ON_NMI=76, /* int: whether we will panic on an unrecovered */
+	KERN_FBSPLASH=77,	/* string: path to fbsplash helper */
+#ifdef CONFIG_ALPHA_UAC_SYSCTL
+	KERN_UAC_POLICY=78,	/* int: Alpha unaligned access control policy flags */
+#endif /* CONFIG_ALPHA_UAC_SYSCTL */
 };
 
 
@@ -258,6 +262,17 @@ enum
 	PTY_NR=2
 };
 
+#ifdef CONFIG_ALPHA_UAC_SYSCTL
+/* /proc/sys/kernel/uac */
+enum
+{
+	/* UAC policy on Alpha */
+	KERN_UAC_NOPRINT=1,	/* int: printk() on unaligned access */
+	KERN_UAC_SIGBUS=2,	/* int: send SIGBUS on unaligned access */
+	KERN_UAC_NOFIX=3,	/* int: don't fix the unaligned access */
+};
+#endif /* CONFIG_ALPHA_UAC_SYSCTL */
+
 /* /proc/sys/bus/isa */
 enum
 {
Index: linux-2.6.22/kernel/sysctl.c
===================================================================
--- linux-2.6.22.orig/kernel/sysctl.c
+++ linux-2.6.22/kernel/sysctl.c
@@ -91,6 +91,9 @@ static int ngroups_max = NGROUPS_MAX;
 #ifdef CONFIG_KMOD
 extern char modprobe_path[];
 #endif
+#ifdef CONFIG_FB_SPLASH
+extern char fbsplash_path[];
+#endif
 #ifdef CONFIG_CHR_DEV_SG
 extern int sg_big_buff;
 #endif
@@ -161,6 +164,9 @@ extern ctl_table pty_table[];
 #ifdef CONFIG_INOTIFY_USER
 extern ctl_table inotify_table[];
 #endif
+#ifdef CONFIG_ALPHA_UAC_SYSCTL
+extern ctl_table uac_table[];
+#endif
 
 #ifdef HAVE_ARCH_PICK_MMAP_LAYOUT
 int sysctl_legacy_va_layout;
@@ -208,7 +214,14 @@ static ctl_table root_table[] = {
 		.mode		= 0555,
 		.child		= dev_table,
 	},
-
+#ifdef CONFIG_ALPHA_UAC_SYSCTL
+	{
+		.ctl_name	= KERN_UAC_POLICY,
+		.procname	= "uac",
+		.mode		= 0555,
+		.child		= uac_table,
+	},
+#endif /* CONFIG_ALPHA_UAC_SYSCTL */
 	{ .ctl_name = 0 }
 };
 
@@ -425,6 +438,17 @@ static ctl_table kern_table[] = {
 		.strategy	= &sysctl_string,
 	},
 #endif
+#ifdef CONFIG_FB_SPLASH
+	{
+		.ctl_name	= KERN_FBSPLASH,
+		.procname	= "fbsplash",
+		.data		= &fbsplash_path,
+		.maxlen		= KMOD_PATH_LEN,
+		.mode		= 0644,
+		.proc_handler	= &proc_dostring,
+		.strategy	= &sysctl_string,
+	},
+#endif
 #ifdef CONFIG_CHR_DEV_SG
 	{
 		.ctl_name	= KERN_SG_BIG_BUFF,
Index: linux-2.6.22/Documentation/fb/vesafb.txt
===================================================================
--- linux-2.6.22.orig/Documentation/fb/vesafb.txt
+++ linux-2.6.22/Documentation/fb/vesafb.txt
@@ -2,16 +2,18 @@
 What is vesafb?
 ===============
 
-This is a generic driver for a graphic framebuffer on intel boxes.
+Vesafb is a generic framebuffer driver for x86 and x86_64 boxes.
 
-The idea is simple:  Turn on graphics mode at boot time with the help
-of the BIOS, and use this as framebuffer device /dev/fb0, like the m68k
-(and other) ports do.
-
-This means we decide at boot time whenever we want to run in text or
-graphics mode.  Switching mode later on (in protected mode) is
-impossible; BIOS calls work in real mode only.  VESA BIOS Extensions
-Version 2.0 are required, because we need a linear frame buffer.
+VESA BIOS Extensions Version 2.0 are required, because we need access to
+a linear frame buffer. VBE 3.0 is required if you want to use modes with a
+higher (than the standard 60 Hz) refresh rate.
+
+The VESA framebuffer driver comes in two flavors - the standard 'vesafb'
+and 'vesafb-tng'. Vesafb-tng is available only on 32-bit x86 due to the
+technology it uses (vm86). Vesafb-tng has more features than vesafb
+(adjusting the refresh rate on VBE 3.0 compliant boards, switching the
+video mode without rebooting, selecting a mode by providing its
+modedb name, and more).
 
 Advantages:
 
@@ -29,26 +31,35 @@ Disadvantages:
 How to use it?
 ==============
 
-Switching modes is done using the vga=... boot parameter.  Read
-Documentation/svga.txt for details.
-
-You should compile in both vgacon (for text mode) and vesafb (for
-graphics mode). Which of them takes over the console depends on
-whenever the specified mode is text or graphics.
-
-The graphic modes are NOT in the list which you get if you boot with
-vga=ask and hit return. The mode you wish to use is derived from the
-VESA mode number. Here are those VESA mode numbers:
+If you are running a 32-bit x86 system and you decide to use vesafb-tng,
+you can either compile the driver into the kernel or use it as a module.
+The graphics mode you want to use is in both cases specified using the
+standard modedb format.
+
+If your system doesn't support vm86 calls, things get a little more tricky.
+Since on such systems you can't do BIOS calls from protected mode in which
+kernel runs, you have to decide at boot time whenever you want to run in text
+or in graphics mode. Switching mode later on is impossible. Switching modes
+is done using the vga=... boot parameter.  Read Documentation/svga.txt for
+details. Below is a more detailed description of what to do on systems using
+the standard vesafb driver.
+
+You should compile in both vgacon (for text mode) and vesafb (for graphics
+mode). Which of them takes over the console depends on whenever the
+specified mode is text or graphics.
+
+The graphic modes are NOT in the list which you get if you boot with vga=ask
+and hit return. The mode you wish to use is derived from the VESA mode number.
+Here are those VESA mode numbers:
 
     | 640x480  800x600  1024x768 1280x1024
 ----+-------------------------------------
-256 |  0x101    0x103    0x105    0x107   
-32k |  0x110    0x113    0x116    0x119   
-64k |  0x111    0x114    0x117    0x11A   
-16M |  0x112    0x115    0x118    0x11B   
+256 |  0x101    0x103    0x105    0x107
+32k |  0x110    0x113    0x116    0x119
+64k |  0x111    0x114    0x117    0x11A
+16M |  0x112    0x115    0x118    0x11B
 
-The video mode number of the Linux kernel is the VESA mode number plus
-0x200.
+The video mode number of the Linux kernel is the VESA mode number plus 0x200.
  
  Linux_kernel_mode_number = VESA_mode_number + 0x200
 
@@ -56,15 +67,15 @@ So the table for the Kernel mode numbers
 
     | 640x480  800x600  1024x768 1280x1024
 ----+-------------------------------------
-256 |  0x301    0x303    0x305    0x307   
-32k |  0x310    0x313    0x316    0x319   
-64k |  0x311    0x314    0x317    0x31A   
-16M |  0x312    0x315    0x318    0x31B   
-
-To enable one of those modes you have to specify "vga=ask" in the
-lilo.conf file and rerun LILO. Then you can type in the desired
-mode at the "vga=ask" prompt. For example if you like to use 
-1024x768x256 colors you have to say "305" at this prompt.
+256 |  0x301    0x303    0x305    0x307
+32k |  0x310    0x313    0x316    0x319
+64k |  0x311    0x314    0x317    0x31A
+16M |  0x312    0x315    0x318    0x31B
+
+To enable one of those modes you have to specify "vga=ask" in the lilo.conf
+file and rerun LILO. Then you can type in the desired mode at the "vga=ask"
+prompt. For example if you like to use 1024x768x256 colors you have to say
+"305" at this prompt.
 
 If this does not work, this might be because your BIOS does not support
 linear framebuffers or because it does not support this mode at all.
@@ -72,11 +83,12 @@ Even if your board does, it might be the
 Extensions v2.0 are required, 1.2 is NOT sufficient.  You will get a
 "bad mode number" message if something goes wrong.
 
-1. Note: LILO cannot handle hex, for booting directly with 
+1. Note: LILO cannot handle hex, for booting directly with
          "vga=mode-number" you have to transform the numbers to decimal.
 2. Note: Some newer versions of LILO appear to work with those hex values,
          if you set the 0x in front of the numbers.
 
+
 X11
 ===
 
@@ -84,98 +96,164 @@ XF68_FBDev should work just fine, but it
 another (accelerated) X-Server like XF86_SVGA might or might not work.
 It depends on X-Server and graphics board.
 
-The X-Server must restore the video mode correctly, else you end up
+The X-Server must restore the video mode correctly, or else you end up
 with a broken console (and vesafb cannot do anything about this).
+With vesafb-tng chances are that the console will be restored properly
+even if the X server messes up the video mode.
 
 
 Refresh rates
 =============
 
-There is no way to change the vesafb video mode and/or timings after
-booting linux.  If you are not happy with the 60 Hz refresh rate, you
-have these options:
-
- * configure and load the DOS-Tools for your the graphics board (if
-   available) and boot linux with loadlin.
- * use a native driver (matroxfb/atyfb) instead if vesafb.  If none
+With VBE 3.0 compatible BIOSes and vesafb-tng it is possible to change
+the refresh rate either at boot time (by specifying the @<rr> part of
+the mode name) or later, using the fbset utility.
+
+If you want to use the default BIOS refresh rate while switching modes
+on a running system, set pixclock to 0.
+
+With VBE 2.0 there is no way to change the mode timings after booting
+Linux. If you are not happy with the 60 Hz refresh rate, you have
+the following options:
+
+ * Configure and load the DOS tools for your the graphics board (if
+   available) and boot Linux with loadlin.
+ * Use a native driver (matroxfb/atyfb) instead of vesafb.  If none
    is available, write a new one!
- * VBE 3.0 might work too.  I have neither a gfx board with VBE 3.0
-   support nor the specs, so I have not checked this yet.
+ * Use a BIOS editor to change the default refresh rate (such an
+   editor does exist at least for ATI Radeon BIOSes).
+ * If you're running a non-vm86 and VBE 3.0 compatible system, you can
+   use a kernel patch (vesafb-rrc) to hard-code some mode timings in
+   the kernel and use these while setting the video mode at boot time.
+
+Note that there are some boards (nVidia 59**, 57** and newer models)
+claiming that their Video BIOS is VBE 3.0 compliant, while ignoring the
+CRTC values provided by software such as vesafb-tng. You'll not be able
+to adjust the refresh rate if you're using one of these boards.
 
 
 Configuration
 =============
 
-The VESA BIOS provides protected mode interface for changing
-some parameters.  vesafb can use it for palette changes and
-to pan the display.  It is turned off by default because it
-seems not to work with some BIOS versions, but there are options
-to turn it on.
-
-You can pass options to vesafb using "video=vesafb:option" on
-the kernel command line.  Multiple options should be separated
-by comma, like this: "video=vesafb:ypan,invers"
-
-Accepted options:
-
-invers	no comment...
-
-ypan	enable display panning using the VESA protected mode 
-	interface.  The visible screen is just a window of the
-	video memory, console scrolling is done by changing the
-	start of the window.
-	pro:	* scrolling (fullscreen) is fast, because there is
-		  no need to copy around data.
-		* You'll get scrollback (the Shift-PgUp thing),
-		  the video memory can be used as scrollback buffer
-	kontra: * scrolling only parts of the screen causes some
-		  ugly flicker effects (boot logo flickers for
-		  example).
-
-ywrap	Same as ypan, but assumes your gfx board can wrap-around 
-	the video memory (i.e. starts reading from top if it
-	reaches the end of video memory).  Faster than ypan.
-
-redraw	scroll by redrawing the affected part of the screen, this
-	is the safe (and slow) default.
-
-
-vgapal	Use the standard vga registers for palette changes.
-	This is the default.
-pmipal	Use the protected mode interface for palette changes.
-
-mtrr:n	setup memory type range registers for the vesafb framebuffer
-	where n:
-	      0 - disabled (equivalent to nomtrr) (default)
-	      1 - uncachable
-	      2 - write-back
-	      3 - write-combining
-	      4 - write-through
+The VESA BIOS provides protected mode interface for changing some parameters.
+vesafb can use it for palette changes and to pan the display. It is turned
+off by default because it seems not to work with some BIOS versions, but
+there are options to turn it on.
+
+You can pass options to vesafb using "video=vesafb:option" on the kernel
+command line. Multiple options should be separated by a comma, like this:
+"video=vesafb:ypan,1024x768-32@85"
+
+Note that vesafb-tng still uses the "video=vesafb:option" format of the
+kernel command line video parameter. "video=vesafb-tng:xxx" is incorrect.
+
+Accepted options (both vesafb and vesafb-tng):
+
+ypan    Enable display panning using the VESA protected mode interface
+        The visible screen is just a window of the video memory,
+        console scrolling is done by changing the start of the window.
+        pro:    * scrolling (fullscreen) is fast, because there is
+                  no need to copy around data.
+                * you'll get scrollback (the Shift-PgUp thing),
+                  the video memory can be used as scrollback buffer
+        con:    * scrolling only parts of the screen causes some
+                  ugly flicker effects (boot logo flickers for
+                  example).
+
+ywrap   Same as ypan, but assumes your gfx board can wrap-around the video
+        memory (i.e. starts reading from top if it reaches the end of
+        video memory). Faster than ypan.
+
+redraw  Scroll by redrawing the affected part of the screen, this is the
+        safe (and slow) default.
+
+vgapal  Use the standard VGA registers for palette changes.
+
+pmipal  Use the protected mode interface for palette changes.
+        This is the default is the protected mode interface is available.
+
+mtrr:n  Setup memory type range registers for the vesafb framebuffer
+        where n:
+              0 - disabled (equivalent to nomtrr) (default)
+              1 - uncachable
+              2 - write-back
+              3 - write-combining
+              4 - write-through
 
-	If you see the following in dmesg, choose the type that matches the
-	old one. In this example, use "mtrr:2".
+        If you see the following in dmesg, choose the type that matches
+        the old one. In this example, use "mtrr:2".
 ...
 mtrr: type mismatch for e0000000,8000000 old: write-back new: write-combining
 ...
 
-nomtrr  disable mtrr
+nomtrr  Do not use memory type range registers for vesafb.
 
 vremap:n
         remap 'n' MiB of video RAM. If 0 or not specified, remap memory
-	according to video mode. (2.5.66 patch/idea by Antonino Daplas
-	reversed to give override possibility (allocate more fb memory
-	than the kernel would) to 2.4 by tmb@iki.fi)
+        according to video mode. (2.5.66 patch/idea by Antonino Daplas
+        reversed to give override possibility (allocate more fb memory
+        than the kernel would) to 2.4 by tmb@iki.fi)
 
 vtotal:n
         if the video BIOS of your card incorrectly determines the total
         amount of video RAM, use this option to override the BIOS (in MiB).
 
-Have fun!
+Options accepted only by vesafb-tng:
 
-  Gerd
+<mode>  The mode you want to set, in the standard modedb format. Refer to
+        modedb.txt for a detailed description. If you specify a mode that is
+        not supported by your board's BIOS, vesafb-tng will attempt to set a
+        similar mode. The list of supported modes can be found in
+        /proc/fbx/modes, where x is the framebuffer number (usually 0).
+        When vesafb-tng is compiled as a module, the mode string should be
+        provided as a value of the parameter 'mode'.
+
+vbemode:x
+        Force the use of VBE mode x. The mode will only be set if it's
+        found in the VBE-provided list of supported modes.
+        NOTE: The mode number 'x' should be specified in VESA mode number
+        notation, not the Linux kernel one (eg. 257 instead of 769).
+        HINT: If you use this option because normal <mode> parameter does
+        not work for you and you use a X server, you'll probably want to
+        set the 'nocrtc' option to ensure that the video mode is properly
+        restored after console <-> X switches.
+
+nocrtc  Do not use CRTC timings while setting the video mode. This option
+        makes sence only with VBE 3.0 compliant systems. Use it if you have
+        problems with modes set in the standard way. Note that using this
+		option means that any refresh rate adjustments will be ignored
+		and the refresh rate will stay at your BIOS default (60 Hz).
+
+noedid  Do not try to fetch and use EDID-provided modes.
+
+noblank Disable hardware blanking.
+
+gtf     Force the use of VESA's GTF (Generalized Timing Formula). Specifying
+        this will cause vesafb to skip its internal modedb and EDID-modedb
+        and jump straight to the GTF part of the code (normally used only if
+        everything else failed). This can be useful if you want to get as
+        much as possible from your graphics board but your BIOS doesn't
+        support modes with the refresh rates you require. Note that you may 
+		need to specify the maxhf, maxvf and maxclk parameters if they are not
+        provided by the EDID block.
+
+Additionally, the following parameters may be provided. They all override the
+EDID-provided values and BIOS defaults. Refer to your monitor's specs to get
+the correct values for maxhf, maxvf and maxclk for your hardware.
+
+maxhf:n     Maximum horizontal frequency (in kHz).
+maxvf:n     Maximum vertical frequency (in Hz).
+maxclk:n    Maximum pixel clock (in MHz).
+
+Have fun!
 
 --
+Original document for the vesafb driver by
 Gerd Knorr <kraxel@goldbach.in-berlin.de>
 
-Minor (mostly typo) changes 
-by Nico Schmoigl <schmoigl@rumms.uni-mannheim.de>
+Minor (mostly typo) changes by
+Nico Schmoigl <schmoigl@rumms.uni-mannheim.de>
+
+Extended documentation for vm86, VBE 3.0 and vesafb-tng by
+Michal Januszewski <spock@gentoo.org>
+
Index: linux-2.6.22/arch/i386/boot/video.S
===================================================================
--- linux-2.6.22.orig/arch/i386/boot/video.S
+++ linux-2.6.22/arch/i386/boot/video.S
@@ -163,10 +163,12 @@ basret:	ret
 # parameters in the default 80x25 mode -- these are set directly,
 # because some very obscure BIOSes supply insane values.
 mode_params:
+#ifdef CONFIG_FB_VESA_STD
 #ifdef CONFIG_VIDEO_SELECT
 	cmpb	$0, graphic_mode
 	jnz	mopar_gr
 #endif
+#endif
 	movb	$0x03, %ah			# Read cursor position
 	xorb	%bh, %bh
 	int	$0x10
@@ -199,6 +201,7 @@ mopar2: movb	%al, %fs:(PARAM_VIDEO_LINES
 	ret
 
 #ifdef CONFIG_VIDEO_SELECT
+#ifdef CONFIG_FB_VESA_STD
 # Fetching of VESA frame buffer parameters
 mopar_gr:
 	leaw	modelist+1024, %di
@@ -281,6 +284,7 @@ dac_done:
 	movw	%es, %fs:(PARAM_VESAPM_SEG)
 	movw	%di, %fs:(PARAM_VESAPM_OFF)
 no_pm:	ret
+#endif
 
 # The video mode menu
 mode_menu:
@@ -495,10 +499,12 @@ mode_set:
 	
 	cmpb	$VIDEO_FIRST_V7>>8, %ah
 	jz	setv7
-	
+
+#ifdef CONFIG_FB_VESA_STD
 	cmpb	$VIDEO_FIRST_VESA>>8, %ah
 	jnc	check_vesa
-	
+#endif	
+
 	orb	%ah, %ah
 	jz	setmenu
 	
@@ -570,6 +576,7 @@ setr1:	lodsw
 	movw	-4(%si), %ax			# Fetch mode ID
 	jmp	_m_s
 
+#ifdef CONFIG_FB_VESA_STD
 check_vesa:
 #ifdef CONFIG_FIRMWARE_EDID
 	leaw	modelist+1024, %di
@@ -613,6 +620,7 @@ check_vesa:
 	ret
 
 _setbad:	jmp	setbad          	# Ugly...
+#endif
 
 # Recalculate vertical display end registers -- this fixes various
 # inconsistencies of extended modes on many adapters. Called when
Index: linux-2.6.22/drivers/char/sysrq.c
===================================================================
--- linux-2.6.22.orig/drivers/char/sysrq.c
+++ linux-2.6.22/drivers/char/sysrq.c
@@ -250,7 +250,7 @@ static void send_sig_all(int sig)
 	struct task_struct *p;
 
 	for_each_process(p) {
-		if (p->mm && !is_init(p))
+		if (p->mm && !is_init(p) && !(p->flags & PF_BORROWED_MM))
 			/* Not swapper, init nor kernel thread */
 			force_sig(sig, p);
 	}
Index: linux-2.6.22/drivers/video/fbmem.c
===================================================================
--- linux-2.6.22.orig/drivers/video/fbmem.c
+++ linux-2.6.22/drivers/video/fbmem.c
@@ -1458,6 +1458,7 @@ fbmem_init(void)
 		printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
 		fb_class = NULL;
 	}
+
 	return 0;
 }
 
Index: linux-2.6.22/drivers/video/modedb.c
===================================================================
--- linux-2.6.22.orig/drivers/video/modedb.c
+++ linux-2.6.22/drivers/video/modedb.c
@@ -671,6 +671,7 @@ void fb_var_to_videomode(struct fb_video
 {
 	u32 pixclock, hfreq, htotal, vtotal;
 
+	mode->refresh = 0;
 	mode->name = NULL;
 	mode->xres = var->xres;
 	mode->yres = var->yres;
@@ -1027,3 +1028,4 @@ EXPORT_SYMBOL(fb_find_best_mode);
 EXPORT_SYMBOL(fb_find_nearest_mode);
 EXPORT_SYMBOL(fb_videomode_to_modelist);
 EXPORT_SYMBOL(fb_find_mode);
+EXPORT_SYMBOL(fb_destroy_modelist);
Index: linux-2.6.22/drivers/video/vesafb-thread.c
===================================================================
--- /dev/null
+++ linux-2.6.22/drivers/video/vesafb-thread.c
@@ -0,0 +1,751 @@
+/*
+ * Framebuffer driver for VBE 2.0+ compliant graphic boards.
+ * Kernel thread and vm86 routines.
+ *
+ * (c) 2004-2006 Michal Januszewski <spock@gentoo.org>
+ *
+ */
+
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/freezer.h>
+#include <linux/suspend.h>
+#include <linux/unistd.h>
+#include <video/vesa.h>
+#include <video/edid.h>
+#include <asm/mman.h>
+#include <asm/page.h>
+#include <asm/vm86.h>
+#include <asm/thread_info.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include "edid.h"
+
+static int errno;
+
+static DECLARE_COMPLETION(vesafb_th_completion);
+static DECLARE_MUTEX(vesafb_task_list_sem);
+static LIST_HEAD(vesafb_task_list);
+static DECLARE_WAIT_QUEUE_HEAD(vesafb_wait);
+
+static struct vm86_struct vm86;
+static int vesafb_pid = 0;
+
+#define DEFAULT_VM86_FLAGS (IF_MASK | IOPL_MASK)
+#define VM86_PUSHW(x)					\
+do { 							\
+	vm86.regs.esp -= 2; 				\
+	*(u16*)(STACK_ADDR + vm86.regs.esp) = x;	\
+} while(0);
+
+/* Stack, the return code and buffers will be put into
+ * one contiguous memory chunk:
+ *
+ * [ STACK | RET_CODE | BUFFER ]
+ *
+ * Some video BIOSes (sis6326) try to store data somewhere
+ * in 0x7000-0x7fff, so we zeromap more memory to be safe.
+ */
+#define IVTBDA_SIZE 	PAGE_SIZE
+#define RET_CODE_SIZE	0x0010
+#define STACK_SIZE	0x0500
+#define BUFFER_SIZE	0x10000
+
+/* The amount of memory that will be allocated should be a multiple
+ * of PAGE_SIZE. */
+#define __MEM_SIZE 	(RET_CODE_SIZE + STACK_SIZE + BUFFER_SIZE)
+#define REAL_MEM_SIZE	(((__MEM_SIZE / PAGE_SIZE) + 1) * PAGE_SIZE)
+
+#define IVTBDA_ADDR	0x00000
+#define STACK_ADDR	(IVTBDA_ADDR + IVTBDA_SIZE)
+#define RET_CODE_ADDR	(STACK_ADDR + STACK_SIZE)
+#define BUF_ADDR	(RET_CODE_ADDR + RET_CODE_SIZE)
+
+#define FLAG_D 		(1 << 10)
+
+/* Syscalls used by the vesafb thread */
+static int vm86old(struct vm86_struct __user* v86)
+{
+	long res;
+	__asm__ volatile ("push %%ebx; movl %2, %%ebx ; int $0x80 ; pop %%ebx"
+			: "=a" (res)
+			: "0" (__NR_vm86old), "ri" ((long)(v86)) : "memory");
+
+	if ((unsigned long)(res) >= (unsigned long)(-MAX_ERRNO)) {
+		errno = -res;
+		res = -1;
+	}
+	return (int)res;
+}
+
+static int ioperm(unsigned long a, unsigned long b, unsigned long c)
+{
+	long res;
+	__asm__ volatile ("push %%ebx; movl %2, %%ebx ; int $0x80 ; pop %%ebx"
+			: "=a" (res)
+			: "0" (__NR_ioperm), "ri" ((long)(a)), "c" ((long)(b)),
+			  "d" ((long)(c)) : "memory");
+
+	if ((unsigned long)(res) >= (unsigned long)(-MAX_ERRNO)) {
+		errno = -res;
+		res = -1;
+	}
+	return (int)res;
+}
+
+/* Segment prefix opcodes */
+enum {
+	P_CS = 0x2e,
+	P_SS = 0x36,
+	P_DS = 0x3e,
+	P_ES = 0x26,
+	P_FS = 0x64,
+	P_GS = 0x65
+};
+
+/* Emulated vm86 ins instruction */
+static void vm86_ins(int size)
+{
+	u32 edx, edi;
+	edx = vm86.regs.edx & 0xffff;
+	edi = (vm86.regs.edi & 0xffff) + (u32)(vm86.regs.es << 4);
+
+	if (vm86.regs.eflags & FLAG_D)
+		asm volatile ("std\n");
+	else
+		asm volatile ("cld\n");
+
+	switch (size) {
+	case 4:
+		asm volatile ("insl\n" : "=D" (edi) : "d" (edx), "0" (edi));
+		break;
+	case 2:
+		asm volatile ("insw\n" : "=D" (edi) : "d" (edx), "0" (edi));
+		break;
+	case 1:
+		asm volatile ("insb\n" : "=D" (edi) : "d" (edx), "0" (edi));
+		break;
+	}
+
+	if (vm86.regs.eflags & FLAG_D)
+		asm volatile ("cld\n");
+
+	edi -= (u32)(vm86.regs.es << 4);
+
+	vm86.regs.edi &= 0xffff0000;
+	vm86.regs.edi |= edi & 0xffff;
+}
+
+static void vm86_rep_ins(int size)
+{
+	u16 cx = vm86.regs.ecx;
+	while (cx--)
+		vm86_ins(size);
+
+	vm86.regs.ecx &= 0xffff0000;
+}
+
+/* Emulated vm86 outs instruction */
+static void vm86_outs(int size, int segment)
+{
+	u32 edx, esi, base;
+
+	edx = vm86.regs.edx & 0xffff;
+	esi = vm86.regs.esi & 0xffff;
+
+	switch (segment) {
+	case P_CS: base = vm86.regs.cs; break;
+	case P_SS: base = vm86.regs.ss; break;
+	case P_ES: base = vm86.regs.es; break;
+	case P_FS: base = vm86.regs.fs; break;
+	case P_GS: base = vm86.regs.gs; break;
+	default:   base = vm86.regs.ds; break;
+	}
+
+	esi += base << 4;
+
+	if (vm86.regs.eflags & FLAG_D)
+		asm volatile ("std\n");
+	else
+		asm volatile ("cld\n");
+
+	switch (size) {
+	case 4:
+		asm volatile ("outsl\n" : "=S" (esi) : "d" (edx), "0" (esi));
+		break;
+	case 2:
+		asm volatile ("outsw\n" : "=S" (esi) : "d" (edx), "0" (esi));
+		break;
+	case 1:
+		asm volatile ("outsb\n" : "=S" (esi) : "d" (edx), "0" (esi));
+		break;
+	}
+
+	if (vm86.regs.eflags & FLAG_D)
+		asm volatile ("cld");
+
+	esi -= base << 4;
+	vm86.regs.esi &= 0xffff0000;
+	vm86.regs.esi |= (esi & 0xffff);
+}
+
+static void vm86_rep_outs(int size, int segment)
+{
+	u16 cx = vm86.regs.ecx;
+	while (cx--)
+		vm86_outs(size, segment);
+
+	vm86.regs.ecx &= 0xffff0000;
+}
+
+static int vm86_do_unknown(void)
+{
+	u8 data32 = 0, segment = P_DS, rep = 0;
+	u8 *instr;
+	int ret = 0, i = 0;
+
+	instr = (u8*)((vm86.regs.cs << 4) + vm86.regs.eip);
+
+	while (1) {
+		switch(instr[i]) {
+		case 0x66:	/* operand size prefix */
+			data32 = 1 - data32;
+			i++;
+			break;
+		case 0xf2:	/* repnz */
+		case 0xf3:	/* rep */
+			rep = 1;
+			i++;
+			break;
+		case P_CS:	/* segment prefix */
+		case P_SS:
+		case P_DS:
+		case P_ES:
+		case P_FS:
+		case P_GS:
+			segment = instr[i];
+			i++;
+			break;
+		case 0xf0:	/* LOCK - ignored */
+		case 0x67:	/* address size prefix - ignored */
+			i++;
+			break;
+		case 0x6c:	/* insb */
+			if (rep)
+				vm86_rep_ins(1);
+			else
+				vm86_ins(1);
+			i++;
+			goto out;
+		case 0x6d:	/* insw / insd */
+			if (rep) {
+				if (data32)
+					vm86_rep_ins(4);
+				else
+					vm86_rep_ins(2);
+			} else {
+				if (data32)
+					vm86_ins(4);
+				else
+					vm86_ins(2);
+			}
+			i++;
+			goto out;
+		case 0x6e:	/* outsb */
+			if (rep)
+				vm86_rep_outs(1, segment);
+			else
+				vm86_outs(1, segment);
+			i++;
+			goto out;
+		case 0x6f:	/* outsw / outsd */
+			if (rep) {
+				if (data32)
+					vm86_rep_outs(4, segment);
+				else
+					vm86_rep_outs(2, segment);
+			} else {
+				if (data32)
+					vm86_outs(4, segment);
+				else
+					vm86_outs(2, segment);
+			}
+			i++;
+			goto out;
+		case 0xe4:	/* inb xx */
+			asm volatile (
+				"inb %w1, %b0"
+				: "=a" (vm86.regs.eax)
+				: "d" (instr[i+1]), "0" (vm86.regs.eax));
+			i += 2;
+			goto out;
+		case 0xe5:	/* inw xx / ind xx */
+			if (data32) {
+				asm volatile (
+					"inl %w1, %0"
+					: "=a" (vm86.regs.eax)
+					: "d" (instr[i+1]),
+					  "0" (vm86.regs.eax));
+			} else {
+				asm volatile (
+					"inw %w1, %w0"
+					: "=a" (vm86.regs.eax)
+					: "d" (instr[i+1]),
+					  "0" (vm86.regs.eax));
+			}
+			i += 2;
+			goto out;
+
+		case 0xec:	/* inb dx */
+			asm volatile (
+				"inb %w1, %b0"
+	 			: "=a" (vm86.regs.eax)
+				: "d" (vm86.regs.edx), "0" (vm86.regs.eax));
+			i++;
+			goto out;
+		case 0xed:	/* inw dx / ind dx */
+			if (data32) {
+				asm volatile (
+					"inl %w1, %0"
+					: "=a" (vm86.regs.eax)
+					: "d" (vm86.regs.edx));
+			} else {
+				asm volatile (
+					"inw %w1, %w0"
+					: "=a" (vm86.regs.eax)
+					: "d" (vm86.regs.edx));
+			}
+			i++;
+			goto out;
+		case 0xe6:	/* outb xx */
+			asm volatile (
+				"outb %b0, %w1"
+				: /* no return value */
+				: "a" (vm86.regs.eax), "d" (instr[i+1]));
+			i += 2;
+			goto out;
+		case 0xe7:	/* outw xx / outd xx */
+			if (data32) {
+				asm volatile (
+					"outl %0, %w1"
+					: /* no return value */
+					: "a" (vm86.regs.eax),
+					  "d" (instr[i+1]));
+			} else {
+				asm volatile (
+					"outw %w0, %w1"
+					: /* no return value */
+					: "a" (vm86.regs.eax),
+					  "d" (instr[i+1]));
+			}
+			i += 2;
+			goto out;
+		case 0xee:	/* outb dx */
+			asm volatile (
+				"outb %b0, %w1"
+				: /* no return value */
+				: "a" (vm86.regs.eax), "d" (vm86.regs.edx));
+			i++;
+			goto out;
+		case 0xef:	/* outw dx / outd dx */
+			if (data32) {
+				asm volatile (
+					"outl %0, %w1"
+					: /* no return value */
+					: "a" (vm86.regs.eax),
+					  "d" (vm86.regs.edx));
+			} else {
+				asm volatile (
+					"outw %w0, %w1"
+					: /* no return value */
+					: "a" (vm86.regs.eax),
+					  "d" (vm86.regs.edx));
+			}
+			i++;
+			goto out;
+		default:
+			printk(KERN_ERR "vesafb: BUG, opcode 0x%x emulation "
+					"not supported (EIP: 0x%lx)\n",
+					instr[i], (u32)(vm86.regs.cs << 4) +
+					vm86.regs.eip);
+			ret = 1;
+			goto out;
+		}
+	}
+out: 	vm86.regs.eip += i;
+	return ret;
+}
+
+void vesafb_do_vm86(struct vm86_regs *regs)
+{
+	unsigned int ret;
+	u8 *retcode = (void*)RET_CODE_ADDR;
+
+	memset(&vm86,0,sizeof(vm86));
+	memcpy(&vm86.regs, regs, sizeof(struct vm86_regs));
+
+	/* The return code */
+	retcode[0] = 0xcd;  		/* int opcode */
+	retcode[1] = 0xff;		/* int number (255) */
+
+        /* We use int 0xff to get back to protected mode */
+	memset(&vm86.int_revectored, 0, sizeof(vm86.int_revectored));
+        ((unsigned char *)&vm86.int_revectored)[0xff / 8] |= (1 << (0xff % 8));
+
+	/*
+	 * We want to call int 0x10, so we set:
+	 *   CS = 0x42 = 0x10 * 4 + 2
+	 *   IP = 0x40 = 0x10 * 4
+	 * and SS:ESP. It's up to the caller to set the rest of the registers.
+	 */
+	vm86.regs.eflags = DEFAULT_VM86_FLAGS;
+	vm86.regs.cs = *(unsigned short *)0x42;
+	vm86.regs.eip = *(unsigned short *)0x40;
+	vm86.regs.ss = (STACK_ADDR >> 4);
+	vm86.regs.esp = ((STACK_ADDR & 0x0000f) + STACK_SIZE);
+
+	/* These will be fetched off the stack when we come to an iret in the
+	 * int's 0x10 code. */
+	VM86_PUSHW(DEFAULT_VM86_FLAGS);
+	VM86_PUSHW((RET_CODE_ADDR >> 4));	/* return code segment */
+	VM86_PUSHW((RET_CODE_ADDR & 0x0000f));	/* return code offset */
+
+	while(1) {
+		ret = vm86old(&vm86);
+
+		if (VM86_TYPE(ret) == VM86_INTx) {
+			int vint = VM86_ARG(ret);
+
+			/* If exit from vm86 was caused by int 0xff, then
+			 * we're done.. */
+			if (vint == 0xff)
+				goto out;
+
+			/* .. otherwise, we have to call the int handler
+			 * manually */
+			VM86_PUSHW(vm86.regs.eflags);
+			VM86_PUSHW(vm86.regs.cs);
+			VM86_PUSHW(vm86.regs.eip);
+
+			vm86.regs.cs = *(u16 *)((vint << 2) + 2);
+			vm86.regs.eip = *(u16 *)(vint << 2);
+			vm86.regs.eflags &= ~(VIF_MASK | TF_MASK);
+		} else if (VM86_TYPE(ret) == VM86_UNKNOWN) {
+			if (vm86_do_unknown())
+				goto out;
+		} else {
+			printk(KERN_ERR "vesafb: BUG, returned from "
+					"vm86 with %x (EIP: 0x%lx)\n",
+					ret, (u32)(vm86.regs.cs << 4) +
+					vm86.regs.eip);
+			goto out;
+		}
+	}
+
+out:	/* copy the registers' state back to the caller's struct */
+	memcpy(regs, &vm86.regs, sizeof(struct vm86_regs));
+}
+
+static int vesafb_remap_pfn_range(unsigned long start, unsigned long end,
+				  unsigned long pgoff, unsigned long prot,
+				  int type)
+{
+	struct vm_area_struct *vma;
+	struct mm_struct *mm = current->mm;
+	int ret = 0;
+
+	vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
+	if (!vma)
+		return -ENOMEM;
+	memset(vma, 0, sizeof(*vma));
+	down_write(&mm->mmap_sem);
+	vma->vm_mm = mm;
+	vma->vm_start = start;
+	vma->vm_end = end;
+	vma->vm_flags = VM_READ | VM_WRITE | VM_EXEC;
+	vma->vm_flags |= mm->def_flags;
+	vma->vm_page_prot.pgprot = prot;
+	vma->vm_pgoff = pgoff;
+
+	if ((ret = insert_vm_struct(mm, vma))) {
+		up_write(&mm->mmap_sem);
+		kmem_cache_free(vm_area_cachep, vma);
+		return ret;
+	}
+
+	if (type) {
+		ret = zeromap_page_range(vma,
+					 vma->vm_start,
+					 vma->vm_end - vma->vm_start,
+					 vma->vm_page_prot);
+	} else {
+		vma->vm_flags |= VM_SHARED;
+		ret = remap_pfn_range(vma,
+				      vma->vm_start,
+				      vma->vm_pgoff,
+				      vma->vm_end - vma->vm_start,
+				      vma->vm_page_prot);
+	}
+	up_write(&mm->mmap_sem);
+	return ret;
+}
+
+static inline int vesafb_init_mem(void)
+{
+	int ret = 0;
+
+	/* The memory chunks we're remapping here should be multiples
+	 * of PAGE_SIZE. */
+	ret += vesafb_remap_pfn_range(0x00000, IVTBDA_SIZE, 0,
+				      PROT_READ | PROT_EXEC | PROT_WRITE, 0);
+	ret += vesafb_remap_pfn_range(IVTBDA_SIZE, REAL_MEM_SIZE, 0,
+				      PROT_READ | PROT_EXEC | PROT_WRITE, 1);
+	ret += vesafb_remap_pfn_range(0x9f000, 0x100000, 
+				      0x9f000 >> PAGE_SHIFT,
+				      PROT_READ | PROT_EXEC | PROT_WRITE, 0);
+	if (ret)
+		printk(KERN_ERR "vesafb thread: memory remapping failed\n");
+
+	return ret;
+}
+
+#define vesafb_get_string(str) \
+{ 									\
+	/* The address is in the form ssssoooo, where oooo = offset,	\
+	 * ssss = segment */						\
+	addr = ((p_vbe(tsk->buf)->str & 0xffff0000) >> 12) +		\
+		(p_vbe(tsk->buf)->str & 0x0000ffff);			\
+									\
+	/* The data is in ROM which is shared between processes, so we 	\
+	 * just translate the real mode address into one visible from 	\
+	 * kernel space */						\
+	if (addr >= 0xa0000) {						\
+		p_vbe(tsk->buf)->str = (u32) __va(addr);		\
+									\
+	/* The data is in the buffer, we just have to convert the	\
+	 * address so that it points into the buffer user provided. */	\
+	} else if (addr > BUF_ADDR && addr < BUF_ADDR +			\
+		   sizeof(struct vesafb_vbe_ib)) {			\
+		addr -= BUF_ADDR;					\
+		p_vbe(tsk->buf)->str = (u32) (tsk->buf + addr);		\
+									\
+	/* This should never happen: someone was insane enough to put	\
+	 * the data somewhere in RAM.. */				\
+	} else {							\
+		p_vbe(tsk->buf)->str = (u32) "";			\
+	}								\
+}
+
+void vesafb_handle_getvbeib(struct vesafb_task *tsk)
+{
+	int addr, res;
+
+	tsk->regs.es  = (BUF_ADDR >> 4);
+	tsk->regs.edi = (BUF_ADDR & 0x000f);
+	strncpy(p_vbe(BUF_ADDR)->vbe_signature, "VBE2", 4);
+
+	vesafb_do_vm86(&tsk->regs);
+	memcpy(tsk->buf, (void*)(BUF_ADDR), sizeof(struct vesafb_vbe_ib));
+
+	/* The OEM fields were not defined prior to VBE 2.0 */
+	if (p_vbe(tsk->buf)->vbe_version >= 0x200) {
+		vesafb_get_string(oem_string_ptr);
+		vesafb_get_string(oem_vendor_name_ptr);
+		vesafb_get_string(oem_product_name_ptr);
+		vesafb_get_string(oem_product_rev_ptr);
+	}
+
+	/* This is basically the same as vesafb_get_string() */
+	addr = ((p_vbe(tsk->buf)->mode_list_ptr & 0xffff0000) >> 12) +
+		(p_vbe(tsk->buf)->mode_list_ptr & 0x0000ffff);
+
+	if (addr >= 0xa0000) {
+		p_vbe(tsk->buf)->mode_list_ptr = (u32) __va(addr);
+	} else if (addr > BUF_ADDR && addr < BUF_ADDR +
+		   sizeof(struct vesafb_vbe_ib)) {
+		addr -= BUF_ADDR;
+		p_vbe(tsk->buf)->mode_list_ptr = (u32) (tsk->buf + addr);
+	} else {
+		res = 0;
+		printk(KERN_WARNING "vesafb: warning, copying modelist "
+				    "from somewhere in RAM!\n");
+		while (*(u16*)(addr+res) != 0xffff &&
+		       res < (sizeof(p_vbe(tsk->buf)->reserved) - 2)) {
+			*(u16*) ((u32)&(p_vbe(tsk->buf)->reserved) + res) =
+				*(u16*)(addr+res);
+			res += 2;
+		}
+		*(u16*) ((u32)&(p_vbe(tsk->buf)->reserved) + res) = 0xffff;
+	}
+}
+
+int vesafb_handle_tasks(void)
+{
+	struct vesafb_task *tsk;
+	struct list_head *curr, *next;
+	int ret = 0;
+
+	down(&vesafb_task_list_sem);
+	list_for_each_safe(curr, next, &vesafb_task_list) {
+		tsk = list_entry(curr, struct vesafb_task, node);
+
+		if (tsk->flags & TF_EXIT) {
+			ret = 1;
+			goto task_done;
+		}
+		if (tsk->flags & TF_GETVBEIB) {
+			vesafb_handle_getvbeib(tsk);
+			goto task_done;
+		}
+		/* Do we need to store a pointer to the buffer in ES:EDI? */
+		if (tsk->flags & TF_BUF_DI) {
+			tsk->regs.es  = (BUF_ADDR >> 4);
+			tsk->regs.edi = (BUF_ADDR & 0x000f);
+		}
+		/* Sometimes the pointer has to be in ES:EBX. */
+		if (tsk->flags & TF_BUF_BX) {
+			tsk->regs.es  = (BUF_ADDR >> 4);
+			tsk->regs.ebx = (BUF_ADDR & 0x000f);
+		}
+		if (tsk->flags & (TF_BUF_DI | TF_BUF_BX))
+			memcpy((void*)BUF_ADDR, tsk->buf, tsk->buf_len);
+
+		vesafb_do_vm86(&tsk->regs);
+
+		if (tsk->flags & TF_RETURN_BUF)
+			memcpy(tsk->buf, (void*)BUF_ADDR, tsk->buf_len);
+
+task_done:	list_del(curr);
+		complete(&tsk->done);
+	}
+
+	/* If we're going to kill this thread, don't allow any elements
+	 * to be added to the task list. */
+	if (!ret)
+		up(&vesafb_task_list_sem);
+
+	return ret;
+}
+
+/*
+ * This 'hybrid' thread serves as a backend for vesafb-tng, handling all vm86
+ * calls. It is started as a kernel thread. It then creates its own mm struct,
+ * thus separating itself from any userspace processes. At this moment, it
+ * stops being a kernel thread (kernel threads have mm = NULL) and becomes
+ * a 'hybrid' thread -- one that has full access to kernel space, yet runs
+ * with its own address space.
+ *
+ * This is necessary because in order to make vm86 calls some parts of the
+ * first 1MB of RAM have to be setup to mimic the real mode. These are:
+ *  - interrupt vector table	[0x00000-0x003ff]
+ *  - BIOS data area		[0x00400-0x004ff]
+ *  - Extended BIOS data area	[0x9fc00-0x9ffff]
+ *  - the video RAM		[0xa0000-0xbffff]
+ *  - video BIOS		[0xc0000-0xcffff]
+ *  - motherboard BIOS		[0xf0000-0xfffff]
+ */
+int vesafb_thread(void *unused)
+{
+	int err = 0;
+
+	set_fs(KERNEL_DS);
+	daemonize("vesafb");
+
+	if (set_new_mm()) {
+		err = -ENOMEM;
+		goto thr_end;
+	}
+	if (vesafb_init_mem()) {
+		err = -ENOMEM;
+		goto thr_end;
+	}
+
+	DPRINTK("started vesafb thread\n");
+
+	/* Having an IO bitmap makes things faster as we avoid GPFs
+	 * when running vm86 code. We can live if it fails, though,
+	 * so don't bother checking for errors. */
+	ioperm(0,1024,1);
+	set_user_nice(current, -10);
+
+	complete(&vesafb_th_completion);
+
+	while (1) {
+		if (vesafb_handle_tasks())
+			break;
+		wait_event_interruptible(vesafb_wait,
+					 !list_empty(&vesafb_task_list));
+		try_to_freeze();
+	}
+
+out:	DPRINTK("exiting the vesafb thread\n");
+	vesafb_pid = -1;
+
+	/* Now that all callers know this thread is no longer running
+	 * (pid < 0), allow them to continue. */
+	up(&vesafb_task_list_sem);
+	return err;
+thr_end:
+	down(&vesafb_task_list_sem);
+	complete(&vesafb_th_completion);
+	goto out;	
+}
+
+int vesafb_queue_task(struct vesafb_task *tsk)
+{
+	down(&vesafb_task_list_sem);
+	if (vesafb_pid < 0)
+		return -1;
+	list_add_tail(&tsk->node, &vesafb_task_list);
+	up(&vesafb_task_list_sem);
+	wake_up(&vesafb_wait);
+	return 0;
+}
+
+int vesafb_wait_for_thread(void)
+{
+	/* PID 0 means that the thread is still initializing. */
+	if (vesafb_pid < 0)
+		return -1;
+	wait_for_completion(&vesafb_th_completion);
+	return 0;
+}
+
+int __init vesafb_init_thread(void)
+{
+	vesafb_pid = kernel_thread(vesafb_thread,NULL,0);
+	return 0;
+}
+
+#ifdef MODULE
+void __exit vesafb_kill_thread(void)
+{
+	struct vesafb_task *tsk;
+	if (vesafb_pid <= 0)
+		return;
+
+	vesafb_create_task(tsk);
+	if (!tsk)
+		return;
+	tsk->flags |= TF_EXIT;
+	vesafb_queue_task(tsk);
+	vesafb_wait_for_task(tsk);
+	kfree(tsk);
+	return;
+}
+module_exit(vesafb_kill_thread);
+#endif
+module_init(vesafb_init_thread);
+
+EXPORT_SYMBOL_GPL(vesafb_queue_task);
+EXPORT_SYMBOL_GPL(vesafb_wait_for_thread);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Januszewski");
+
Index: linux-2.6.22/drivers/video/vesafb-tng.c
===================================================================
--- /dev/null
+++ linux-2.6.22/drivers/video/vesafb-tng.c
@@ -0,0 +1,1586 @@
+/*
+ * Framebuffer driver for VBE 2.0+ compliant graphic boards
+ *
+ * (c) 2004-2006 Michal Januszewski <spock@gentoo.org>
+ *     Based upon vesafb code by Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <video/edid.h>
+#include <video/vesa.h>
+#include <video/vga.h>
+#include <asm/io.h>
+#include <asm/mtrr.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include "edid.h"
+
+#define dac_reg	(0x3c8)
+#define dac_val	(0x3c9)
+
+#define VESAFB_NEED_EXACT_RES 	1
+#define VESAFB_NEED_EXACT_DEPTH 2
+
+/* --------------------------------------------------------------------- */
+
+static struct fb_var_screeninfo vesafb_defined __initdata = {
+	.activate	= FB_ACTIVATE_NOW,
+	.height		= 0,
+	.width		= 0,
+	.right_margin	= 32,
+	.upper_margin	= 16,
+	.lower_margin	= 4,
+	.vsync_len	= 4,
+	.vmode		= FB_VMODE_NONINTERLACED,
+};
+
+static struct fb_fix_screeninfo vesafb_fix __initdata = {
+	.id	= "VESA VGA",
+	.type	= FB_TYPE_PACKED_PIXELS,
+	.accel	= FB_ACCEL_NONE,
+};
+
+static int  mtrr       = 0;	/* disable mtrr by default */
+static int  blank      = 1;     /* enable blanking by default */
+static int  ypan       = 0;	/* 0 - nothing, 1 - ypan, 2 - ywrap */
+static int  pmi_setpal = 1;	/* pmi for palette changes */
+static u16  *pmi_base  = NULL;  /* protected mode interface location */
+static void (*pmi_start)(void) = NULL;
+static void (*pmi_pal)(void)   = NULL;
+static struct vesafb_vbe_ib  vbe_ib;
+static struct vesafb_mode_ib *vbe_modes;
+static int                   vbe_modes_cnt = 0;
+static struct fb_info	     *vesafb_info = NULL;
+static int  nocrtc		    = 0; /* ignore CRTC settings */
+static int  noedid       __initdata = 0; /* don't try DDC transfers */
+static int  vram_remap   __initdata = 0; /* set amount of memory to be used */
+static int  vram_total   __initdata = 0; /* set total amount of memory */
+static u16  maxclk       __initdata = 0; /* maximum pixel clock */
+static u16  maxvf        __initdata = 0; /* maximum vertical frequency */
+static u16  maxhf        __initdata = 0; /* maximum horizontal frequency */
+static int  gtf          __initdata = 0; /* forces use of the GTF */
+static char *mode_option __initdata = NULL;
+static u16  vbemode      __initdata = 0;
+
+/* --------------------------------------------------------------------- */
+
+static int vesafb_find_vbe_mode(int xres, int yres, int depth,
+				unsigned char flags)
+{
+	int i, match = -1, h = 0, d = 0x7fffffff;
+
+	for (i = 0; i < vbe_modes_cnt; i++) {
+		h = abs(vbe_modes[i].x_res - xres) +
+		    abs(vbe_modes[i].y_res - yres) +
+		    abs(depth - vbe_modes[i].depth);
+		if (h == 0)
+			return i;
+		if (h < d || (h == d && vbe_modes[i].depth > depth)) {
+			d = h;
+			match = i;
+		}
+	}
+	i = 1;
+
+	if (flags & VESAFB_NEED_EXACT_DEPTH && vbe_modes[match].depth != depth)
+		i = 0;
+	if (flags & VESAFB_NEED_EXACT_RES && d > 24)
+		i = 0;
+	if (i != 0)
+		return match;
+	else
+		return -1;
+}
+
+static int vesafb_pan_display(struct fb_var_screeninfo *var,
+                              struct fb_info *info)
+{
+	int offset;
+
+	offset = (var->yoffset * info->fix.line_length + var->xoffset) / 4;
+
+	/* It turns out it's not the best idea to do panning via vm86,
+	 * so we only allow it if we have a PMI. */
+	if (pmi_start) {
+		__asm__ __volatile__(
+			"call *(%%edi)"
+			: /* no return value */
+			: "a" (0x4f07),         /* EAX */
+			  "b" (0),              /* EBX */
+			  "c" (offset),         /* ECX */
+			  "d" (offset >> 16),   /* EDX */
+			  "D" (&pmi_start));    /* EDI */
+	}
+	return 0;
+}
+
+static int vesafb_blank(int blank, struct fb_info *info)
+{
+	struct vesafb_task *tsk;
+	int err = 1;
+
+	if (vbe_ib.capabilities & VBE_CAP_VGACOMPAT) {
+		int loop = 10000;
+		u8 seq = 0, crtc17 = 0;
+
+		if (blank == FB_BLANK_POWERDOWN) {
+			seq = 0x20;
+			crtc17 = 0x00;
+			err = 0;
+		} else {
+			seq = 0x00;
+			crtc17 = 0x80;
+			err = (blank == FB_BLANK_UNBLANK) ? 0 : -EINVAL;
+		}
+
+		vga_wseq(NULL, 0x00, 0x01);
+		seq |= vga_rseq(NULL, 0x01) & ~0x20;
+		vga_wseq(NULL, 0x00, seq);
+
+		crtc17 |= vga_rcrt(NULL, 0x17) & ~0x80;
+		while (loop--);
+		vga_wcrt(NULL, 0x17, crtc17);
+		vga_wseq(NULL, 0x00, 0x03);
+	} else {
+		vesafb_create_task (tsk);
+		if (!tsk)
+			return -ENOMEM;
+		tsk->regs.eax = 0x4f10;
+		switch (blank) {
+		case FB_BLANK_UNBLANK:
+			tsk->regs.ebx = 0x0001;
+			break;
+		case FB_BLANK_NORMAL:
+			tsk->regs.ebx = 0x0101;	/* standby */
+			break;
+		case FB_BLANK_POWERDOWN:
+			tsk->regs.ebx = 0x0401;	/* powerdown */
+			break;
+		default:
+			goto out;
+		}
+		tsk->flags = TF_CALL;
+		if (!vesafb_queue_task (tsk))
+			vesafb_wait_for_task(tsk);
+
+		if ((tsk->regs.eax & 0xffff) == 0x004f)
+			err = 0;
+out:		kfree(tsk);
+	}
+	return err;
+}
+
+static int vesafb_setpalette(struct vesafb_pal_entry *entries, int count,
+			     int start, struct fb_info *info)
+{
+	struct vesafb_task *tsk;
+	int i = ((struct vesafb_par*)info->par)->mode_idx;
+	int ret = 0;
+
+	/* We support palette modifications for 8 bpp modes only, so
+	 * there can never be more than 256 entries. */
+	if (start + count > 256)
+		return -EINVAL;
+
+	/* Use VGA registers if mode is VGA-compatible. */
+	if (i >= 0 && i < vbe_modes_cnt &&
+	    vbe_modes[i].mode_attr & VBE_MODE_VGACOMPAT) {
+		for (i = 0; i < count; i++) {
+			outb_p(start + i,        dac_reg);
+			outb_p(entries[i].red,   dac_val);
+			outb_p(entries[i].green, dac_val);
+			outb_p(entries[i].blue,  dac_val);
+		}
+	} else if (pmi_setpal) {
+		__asm__ __volatile__(
+		"call *(%%esi)"
+		: /* no return value */
+		: "a" (0x4f09),         /* EAX */
+		  "b" (0),              /* EBX */
+		  "c" (count),          /* ECX */
+		  "d" (start),          /* EDX */
+		  "D" (entries),        /* EDI */
+		  "S" (&pmi_pal));      /* ESI */
+	} else {
+		vesafb_create_task (tsk);
+		if (!tsk)
+			return -ENOMEM;
+		tsk->regs.eax = 0x4f09;
+		tsk->regs.ebx = 0x0;
+		tsk->regs.ecx = count;
+		tsk->regs.edx = start;
+		tsk->buf = entries;
+		tsk->buf_len = sizeof(struct vesafb_pal_entry) * count;
+		tsk->flags = TF_CALL | TF_BUF_DI;
+
+		if (!vesafb_queue_task (tsk))
+			vesafb_wait_for_task(tsk);
+		if ((tsk->regs.eax & 0xffff) != 0x004f)
+			ret = 1;
+		kfree(tsk);
+	}
+	return ret;
+}
+
+static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			    unsigned blue, unsigned transp,
+			    struct fb_info *info)
+{
+	struct vesafb_pal_entry entry;
+	int shift = 16 - info->var.green.length;
+	int ret = 0;
+
+	if (regno >= info->cmap.len)
+		return -EINVAL;
+
+	if (info->var.bits_per_pixel == 8) {
+		entry.red   = red   >> shift;
+		entry.green = green >> shift;
+		entry.blue  = blue  >> shift;
+		entry.pad   = 0;
+
+		ret = vesafb_setpalette(&entry, 1, regno, info);
+	} else if (regno < 16) {
+		switch (info->var.bits_per_pixel) {
+		case 16:
+			if (info->var.red.offset == 10) {
+				/* 1:5:5:5 */
+				((u32*) (info->pseudo_palette))[regno] =
+						((red   & 0xf800) >>  1) |
+						((green & 0xf800) >>  6) |
+						((blue  & 0xf800) >> 11);
+			} else {
+				/* 0:5:6:5 */
+				((u32*) (info->pseudo_palette))[regno] =
+						((red   & 0xf800)      ) |
+						((green & 0xfc00) >>  5) |
+						((blue  & 0xf800) >> 11);
+			}
+			break;
+
+		case 24:
+		case 32:
+			red   >>= 8;
+			green >>= 8;
+			blue  >>= 8;
+			((u32 *)(info->pseudo_palette))[regno] =
+				(red   << info->var.red.offset)   |
+				(green << info->var.green.offset) |
+				(blue  << info->var.blue.offset);
+			break;
+		}
+	}
+	return ret;
+}
+
+static int vesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+	struct vesafb_pal_entry *entries;
+	int shift = 16 - info->var.green.length;
+	int i, ret = 0;
+
+	if (info->var.bits_per_pixel == 8) {
+		if (cmap->start + cmap->len > info->cmap.start +
+		    info->cmap.len || cmap->start < info->cmap.start)
+			return -EINVAL;
+
+		entries = vmalloc(sizeof(struct vesafb_pal_entry) * cmap->len);
+		if (!entries)
+			return -ENOMEM;
+		for (i = 0; i < cmap->len; i++) {
+			entries[i].red   = cmap->red[i]   >> shift;
+			entries[i].green = cmap->green[i] >> shift;
+			entries[i].blue  = cmap->blue[i]  >> shift;
+			entries[i].pad   = 0;
+		}
+		ret = vesafb_setpalette(entries, cmap->len, cmap->start, info);
+		vfree(entries);
+	} else {
+		/* For modes with bpp > 8, we only set the pseudo palette in
+		 * the fb_info struct. We rely on vesafb_setcolreg to do all
+		 * sanity checking. */
+		for (i = 0; i < cmap->len; i++) {
+			ret += vesafb_setcolreg(cmap->start + i, cmap->red[i],
+						cmap->green[i], cmap->blue[i],
+						0, info);
+		}
+	}
+	return ret;
+}
+
+static int vesafb_set_par(struct fb_info *info)
+{
+	struct vesafb_par *par = (struct vesafb_par *) info->par;
+	struct vesafb_task *tsk;
+	struct vesafb_crtc_ib *crtc = NULL;
+	struct vesafb_mode_ib *mode = NULL;
+	int i, err = 0, depth = info->var.bits_per_pixel;
+
+	if (depth > 8 && depth != 32)
+		depth = info->var.red.length + info->var.green.length +
+			info->var.blue.length;
+
+	i = vesafb_find_vbe_mode(info->var.xres, info->var.yres, depth,
+				 VESAFB_NEED_EXACT_RES |
+				 VESAFB_NEED_EXACT_DEPTH);
+	if (i >= 0)
+		mode = &vbe_modes[i];
+	else
+		return -EINVAL;
+
+	vesafb_create_task (tsk);
+	if (!tsk)
+		return -ENOMEM;
+	tsk->regs.eax = 0x4f02;
+	tsk->regs.ebx = mode->mode_id | 0x4000;		/* use LFB */
+	tsk->flags = TF_CALL;
+
+	if (vbe_ib.vbe_version >= 0x0300 && !nocrtc &&
+	    info->var.pixclock != 0) {
+		tsk->regs.ebx |= 0x0800; 		/* use CRTC data */
+		tsk->flags |= TF_BUF_DI;
+		crtc = kmalloc(sizeof(struct vesafb_crtc_ib), GFP_KERNEL);
+		if (!crtc) {
+			err = -ENOMEM;
+			goto out;
+		}
+		crtc->horiz_start = info->var.xres + info->var.right_margin;
+		crtc->horiz_end	  = crtc->horiz_start + info->var.hsync_len;
+		crtc->horiz_total = crtc->horiz_end + info->var.left_margin;
+
+		crtc->vert_start  = info->var.yres + info->var.lower_margin;
+		crtc->vert_end    = crtc->vert_start + info->var.vsync_len;
+		crtc->vert_total  = crtc->vert_end + info->var.upper_margin;
+
+		crtc->pixel_clock = PICOS2KHZ(info->var.pixclock) * 1000;
+		crtc->refresh_rate = (u16)(100 * (crtc->pixel_clock /
+				     (crtc->vert_total * crtc->horiz_total)));
+		crtc->flags = 0;
+
+		if (info->var.vmode & FB_VMODE_DOUBLE)
+			crtc->flags |= 0x1;
+		if (info->var.vmode & FB_VMODE_INTERLACED)
+			crtc->flags |= 0x2;
+		if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
+			crtc->flags |= 0x4;
+		if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
+			crtc->flags |= 0x8;
+		memcpy(&par->crtc, crtc, sizeof(struct vesafb_crtc_ib));
+	} else
+		memset(&par->crtc, 0, sizeof(struct vesafb_crtc_ib));
+
+	tsk->buf = (void*)crtc;
+	tsk->buf_len = sizeof(struct vesafb_crtc_ib);
+
+	if (vesafb_queue_task (tsk)) {
+		err = -EINVAL;
+		goto out;
+	}
+	vesafb_wait_for_task(tsk);
+
+	if ((tsk->regs.eax & 0xffff) != 0x004f) {
+		printk(KERN_ERR "vesafb: mode switch failed (eax: 0x%lx)\n",
+				tsk->regs.eax);
+		err = -EINVAL;
+		goto out;
+	}
+	par->mode_idx = i;
+
+	/* For 8bpp modes, always try to set the DAC to 8 bits. */
+	if (vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC &&
+	    mode->bits_per_pixel <= 8) {
+		vesafb_reset_task(tsk);
+		tsk->flags = TF_CALL;
+		tsk->regs.eax = 0x4f08;
+		tsk->regs.ebx = 0x0800;
+
+		if (!vesafb_queue_task (tsk))
+			vesafb_wait_for_task(tsk);
+
+		if ((tsk->regs.eax & 0xffff) != 0x004f ||
+		    ((tsk->regs.ebx & 0xff00) >> 8) != 8) {
+			/* We've failed to set the DAC palette format -
+			 * time to correct var. */
+			info->var.red.length    = 6;
+			info->var.green.length  = 6;
+			info->var.blue.length   = 6;
+		}
+	}
+
+	info->fix.visual = (info->var.bits_per_pixel == 8) ?
+		           FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+	info->fix.line_length = mode->bytes_per_scan_line;
+
+	DPRINTK("set new mode %dx%d-%d (0x%x)\n",
+		info->var.xres, info->var.yres, info->var.bits_per_pixel,
+		mode->mode_id);
+
+out:	if (crtc != NULL)
+		kfree(crtc);
+	kfree(tsk);
+
+	return err;
+}
+
+static void vesafb_setup_var(struct fb_var_screeninfo *var, struct fb_info *info,
+			     struct vesafb_mode_ib *mode)
+{
+	var->xres = mode->x_res;
+	var->yres = mode->y_res;
+	var->xres_virtual = mode->x_res;
+	var->yres_virtual = (ypan) ?
+			      info->fix.smem_len / mode->bytes_per_scan_line :
+			      mode->y_res;
+	var->xoffset = 0;
+	var->yoffset = 0;
+	var->bits_per_pixel = mode->bits_per_pixel;
+
+	if (var->bits_per_pixel == 15)
+		var->bits_per_pixel = 16;
+
+	if (var->bits_per_pixel > 8) {
+		var->red.offset    = mode->red_off;
+		var->red.length    = mode->red_len;
+		var->green.offset  = mode->green_off;
+		var->green.length  = mode->green_len;
+		var->blue.offset   = mode->blue_off;
+		var->blue.length   = mode->blue_len;
+		var->transp.offset = mode->rsvd_off;
+		var->transp.length = mode->rsvd_len;
+
+		DPRINTK("directcolor: size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
+			mode->rsvd_len,
+			mode->red_len,
+			mode->green_len,
+			mode->blue_len,
+			mode->rsvd_off,
+			mode->red_off,
+			mode->green_off,
+			mode->blue_off);
+	} else {
+		var->red.offset    = 0;
+		var->green.offset  = 0;
+		var->blue.offset   = 0;
+		var->transp.offset = 0;
+
+		/* We're assuming that we can switch the DAC to 8 bits. If
+		 * this proves to be incorrect, we'll update the fields
+		 * later in set_par(). */
+		if (vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC) {
+			var->red.length    = 8;
+			var->green.length  = 8;
+			var->blue.length   = 8;
+			var->transp.length = 0;
+		} else {
+			var->red.length    = 6;
+			var->green.length  = 6;
+			var->blue.length   = 6;
+			var->transp.length = 0;
+		}
+	}
+}
+
+static void inline vesafb_check_limits(struct fb_var_screeninfo *var,
+		 		       struct fb_info *info)
+{
+	struct fb_videomode *mode;
+
+	if (!var->pixclock)
+		return;
+	if (vbe_ib.vbe_version < 0x0300) {
+		fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, var, info);
+		return;
+	}
+	if (!fb_validate_mode(var, info))
+		return;
+	mode = fb_find_best_mode(var, &info->modelist);
+	if (mode) {
+		DPRINTK("find_best_mode: %d %d @ %d (vmode: %d)\n",
+			mode->xres, mode->yres, mode->refresh, mode->vmode);
+		if (mode->xres == var->xres && mode->yres == var->yres &&
+		    !(mode->vmode & (FB_VMODE_INTERLACED | FB_VMODE_DOUBLE))) {
+			fb_videomode_to_var(var, mode);
+			return;
+		}
+	}
+	if (info->monspecs.gtf && !fb_get_mode(FB_MAXTIMINGS, 0, var, info))
+		return;
+	/* Use default refresh rate */
+	var->pixclock = 0;
+}
+
+static int vesafb_check_var(struct fb_var_screeninfo *var,
+			    struct fb_info *info)
+{
+	int match = -1;
+	int depth = var->red.length + var->green.length + var->blue.length;
+
+	/* Various apps will use bits_per_pixel to set the color depth,
+	 * which is theoretically incorrect, but which we'll try to handle
+	 * here. */
+	if (depth == 0 || abs(depth - var->bits_per_pixel) >= 8)
+		depth = var->bits_per_pixel;
+	match = vesafb_find_vbe_mode(var->xres, var->yres, depth,
+				     VESAFB_NEED_EXACT_RES);
+
+	if (match == -1) {
+		DPRINTK("vesafb: mode %dx%d-%d not found\n", var->xres,
+			var->yres, depth);
+		return -EINVAL;
+	}
+
+	vesafb_setup_var(var, info, &vbe_modes[match]);
+	DPRINTK("found mode 0x%x (%dx%d-%dbpp)\n",
+		vbe_modes[match].mode_id, vbe_modes[match].x_res,
+		vbe_modes[match].y_res, vbe_modes[match].depth);
+
+	/* Check whether we have remapped enough memory for this mode. */
+	if (var->yres * vbe_modes[match].bytes_per_scan_line >
+	    info->fix.smem_len) {
+		return -EINVAL;
+	}
+
+	if ((var->vmode & FB_VMODE_DOUBLE) &&
+	    !(vbe_modes[match].mode_attr & 0x100))
+		var->vmode &= ~FB_VMODE_DOUBLE;
+	if ((var->vmode & FB_VMODE_INTERLACED) &&
+	    !(vbe_modes[match].mode_attr & 0x200))
+		var->vmode &= ~FB_VMODE_INTERLACED;
+	vesafb_check_limits(var, info);
+	return 0;
+}
+
+static int vesafb_open(struct fb_info *info, int user)
+{
+	struct vesafb_task *tsk = NULL;
+	struct vesafb_par *par = info->par;
+	int cnt = atomic_read(&par->ref_count);
+
+	if (!cnt) {
+		vesafb_create_task(tsk);
+		if (!tsk)
+			goto out;
+
+		/* Get the VBE state buffer size. We want all available
+		 * hardware state data (CL = 0x0f). */
+		tsk->regs.eax = 0x4f04;
+		tsk->regs.ecx = 0x000f;
+		tsk->regs.edx = 0x0000;
+		tsk->flags = TF_CALL;
+
+		if (vesafb_queue_task(tsk))
+			goto out;
+	
+		vesafb_wait_for_task(tsk);
+		
+		if ((tsk->regs.eax & 0xffff) != 0x004f) {
+			printk(KERN_WARNING "vesafb: VBE state buffer size "
+				"cannot be determined (eax: 0x%lx)\n",
+				tsk->regs.eax);
+			goto out;
+		}
+
+		par->vbe_state_size = 64 * (tsk->regs.ebx & 0xffff);
+		par->vbe_state = kzalloc(par->vbe_state_size, GFP_KERNEL);
+		if (!par->vbe_state) 
+			goto out;
+
+		vesafb_reset_task(tsk);
+		tsk->regs.eax = 0x4f04;
+		tsk->regs.ecx = 0x000f;
+		tsk->regs.edx = 0x0001;
+		tsk->flags = TF_CALL | TF_BUF_BX | TF_RETURN_BUF;
+		tsk->buf = (void*)(par->vbe_state);
+		tsk->buf_len = par->vbe_state_size;
+
+		if (vesafb_queue_task(tsk))
+			goto getstate_failed;
+		vesafb_wait_for_task(tsk);
+
+		if ((tsk->regs.eax & 0xffff) != 0x004f) {
+			printk(KERN_WARNING "vesafb: VBE get state call "
+				"failed (eax: 0x%lx)\n", tsk->regs.eax);
+			goto getstate_failed;
+		}
+	}
+out:
+	atomic_inc(&par->ref_count);
+	if (tsk)
+		kfree(tsk);
+	return 0;
+
+getstate_failed:
+	kfree(par->vbe_state);
+	par->vbe_state = NULL;
+	par->vbe_state_size = 0;
+	goto out;
+}
+
+static int vesafb_release(struct fb_info *info, int user)
+{
+	struct vesafb_task *tsk = NULL;
+	struct vesafb_par *par = info->par;
+	int cnt = atomic_read(&par->ref_count);
+
+	if (!cnt)
+		return -EINVAL;
+	
+	if (cnt == 1 && par->vbe_state && par->vbe_state_size) {
+		vesafb_create_task(tsk);
+		if (!tsk)
+			goto out;
+
+		tsk->regs.eax = 0x0003;
+		tsk->regs.ebx = 0x0000;
+		tsk->flags = TF_CALL;
+
+		if (vesafb_queue_task(tsk))
+			goto out;
+	
+		vesafb_wait_for_task(tsk);
+
+		vesafb_reset_task(tsk);
+		tsk->regs.eax = 0x4f04;
+		tsk->regs.ecx = 0x000f;
+		tsk->regs.edx = 0x0002;
+		tsk->buf = (void*)(par->vbe_state);
+		tsk->buf_len = par->vbe_state_size;
+		tsk->flags = TF_CALL | TF_BUF_BX;
+
+		if (vesafb_queue_task(tsk))
+			goto out;
+	
+		vesafb_wait_for_task(tsk);
+
+		if ((tsk->regs.eax & 0xffff) != 0x004f)
+			printk(KERN_WARNING "vesafb: VBE state restore call "
+				"failed (eax: 0x%lx)\n",
+				tsk->regs.eax);
+	}
+out:
+	atomic_dec(&par->ref_count);
+	if (tsk)
+		kfree(tsk);
+	return 0;
+}
+
+static int __init vesafb_probe(struct platform_device *device);
+
+static struct fb_ops vesafb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_open	= vesafb_open,
+	.fb_release	= vesafb_release,
+	.fb_setcolreg	= vesafb_setcolreg,
+	.fb_setcmap	= vesafb_setcmap,
+	.fb_pan_display	= vesafb_pan_display,
+	.fb_blank       = vesafb_blank,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_check_var	= vesafb_check_var,
+	.fb_set_par	= vesafb_set_par
+};
+
+static struct platform_driver vesafb_driver = {
+	.probe	= vesafb_probe,
+	.driver	= {
+		.name	= "vesafb",
+	},
+};
+
+static struct platform_device *vesafb_device;
+ 
+#ifndef MODULE
+int __init vesafb_setup(char *options)
+{
+	char *this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	DPRINTK("options %s\n",options);
+
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+		if (!*this_opt) continue;
+
+		DPRINTK("this_opt: %s\n",this_opt);
+
+		if (! strcmp(this_opt, "redraw"))
+			ypan=0;
+		else if (! strcmp(this_opt, "ypan"))
+			ypan=1;
+		else if (! strcmp(this_opt, "ywrap"))
+			ypan=2;
+		else if (! strcmp(this_opt, "vgapal"))
+			pmi_setpal=0;
+		else if (! strcmp(this_opt, "pmipal"))
+			pmi_setpal=1;
+		else if (! strncmp(this_opt, "mtrr:", 5))
+			mtrr = simple_strtoul(this_opt+5, NULL, 0);
+		else if (! strcmp(this_opt, "nomtrr"))
+			mtrr=0;
+		else if (! strcmp(this_opt, "nocrtc"))
+			nocrtc=1;
+		else if (! strcmp(this_opt, "noedid"))
+			noedid=1;
+		else if (! strcmp(this_opt, "noblank"))
+			blank=0;
+		else if (! strcmp(this_opt, "gtf"))
+			gtf=1;
+		else if (! strncmp(this_opt, "vtotal:", 7))
+			vram_total = simple_strtoul(this_opt + 7, NULL, 0);
+		else if (! strncmp(this_opt, "vremap:", 7))
+			vram_remap = simple_strtoul(this_opt + 7, NULL, 0);
+		else if (! strncmp(this_opt, "maxhf:", 6))
+			maxhf = simple_strtoul(this_opt + 6, NULL, 0);
+		else if (! strncmp(this_opt, "maxvf:", 6))
+			maxvf = simple_strtoul(this_opt + 6, NULL, 0);
+		else if (! strncmp(this_opt, "maxclk:", 7))
+			maxclk = simple_strtoul(this_opt + 7, NULL, 0);
+		else if (! strncmp(this_opt, "vbemode:", 8))
+			vbemode = simple_strtoul(this_opt + 8, NULL,0);
+		else if (this_opt[0] >= '0' && this_opt[0] <= '9') {
+			DPRINTK("mode_option: %s\n",this_opt);
+			mode_option = this_opt;
+		} else {
+			printk(KERN_WARNING
+			       "vesafb: unrecognized option %s\n", this_opt);
+		}
+	}
+
+	return 0;
+}
+#endif /* !MODULE */
+
+static int vesafb_read_proc_modes(char *buf, char **start, off_t offset,
+			    	  int len, int *eof, void *private)
+{
+	int clen = 0, i;
+
+	for (i = 0; i < vbe_modes_cnt; i++) {
+		clen += min(snprintf(buf + clen, len - clen, "%dx%d-%d\n", vbe_modes[i].x_res,
+				vbe_modes[i].y_res, vbe_modes[i].depth), len - clen);
+	}
+	*eof = 1;
+	return clen;
+}
+
+static int vesafb_read_proc_vbe_info(char *buf, char **start, off_t offset,
+			    	     int len, int *eof, void *private)
+{
+	int clen = 0;
+
+	clen += min(snprintf(buf + clen, len, "Version:    %d.%d\n",
+			((vbe_ib.vbe_version & 0xff00) >> 8),
+			vbe_ib.vbe_version & 0xff), len);
+	clen += min(snprintf(buf + clen, len - clen, "Vendor:     %s\n",
+			(char*)vbe_ib.oem_vendor_name_ptr), len - clen);
+	clen += min(snprintf(buf + clen, len - clen, "Product:    %s\n",
+			(char*)vbe_ib.oem_product_name_ptr), len - clen);
+	clen += min(snprintf(buf + clen, len - clen, "OEM rev:    %s\n",
+			(char*)vbe_ib.oem_product_rev_ptr), len - clen);
+	clen += min(snprintf(buf + clen, len - clen, "OEM string: %s\n",
+			(char*)vbe_ib.oem_string_ptr), len - clen);
+
+	*eof = 1;
+	return clen;
+}
+
+static int __init inline vesafb_vbe_getinfo(struct vesafb_task *tsk)
+{
+	tsk->regs.eax = 0x4f00;
+	tsk->flags = TF_CALL | TF_GETVBEIB;
+	tsk->buf = &vbe_ib;
+	tsk->buf_len = sizeof(vbe_ib);
+	if (vesafb_queue_task (tsk))
+		return -EINVAL;
+	vesafb_wait_for_task(tsk);
+
+	if (vbe_ib.vbe_version < 0x0200) {
+		printk(KERN_ERR "vesafb: Sorry, pre-VBE 2.0 cards are "
+				"not supported.\n");
+		return -EINVAL;
+	}
+
+	if ((tsk->regs.eax & 0xffff) != 0x004f) {
+		printk(KERN_ERR "vesafb: Getting mode info block failed "
+				"(eax=0x%x)\n", (u32)tsk->regs.eax);
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "vesafb: %s, %s, %s (OEM: %s)\n",
+		(char*)vbe_ib.oem_vendor_name_ptr,
+		(char*)vbe_ib.oem_product_name_ptr,
+		(char*)vbe_ib.oem_product_rev_ptr,
+		(char*)vbe_ib.oem_string_ptr);
+
+	printk(KERN_INFO "vesafb: VBE version: %d.%d\n",
+			 ((vbe_ib.vbe_version & 0xff00) >> 8),
+			 vbe_ib.vbe_version & 0xff);
+	return 0;
+}
+
+static int __init inline vesafb_vbe_getmodes(struct vesafb_task *tsk)
+{
+	u16 *mode = 0;
+	int off = 0;
+
+	/* Count available modes. */
+	mode = (u16*)vbe_ib.mode_list_ptr;
+	while (*mode != 0xffff) {
+		vbe_modes_cnt++;
+		mode++;
+	}
+
+	vbe_modes = kmalloc(sizeof(struct vesafb_mode_ib)*
+			    vbe_modes_cnt, GFP_KERNEL);
+	if (!vbe_modes)
+		return -ENOMEM;
+
+	/* Get mode info for all available modes. */
+	mode = (u16*)vbe_ib.mode_list_ptr;
+
+	while (*mode != 0xffff) {
+		struct vesafb_mode_ib *mib;
+
+		vesafb_reset_task(tsk);
+		tsk->regs.eax = 0x4f01;
+		tsk->regs.ecx = (u32) *mode;
+		tsk->flags = TF_CALL | TF_RETURN_BUF | TF_BUF_DI;
+		tsk->buf = vbe_modes+off;
+		tsk->buf_len = sizeof(struct vesafb_mode_ib);
+		if (vesafb_queue_task(tsk))
+			return -EINVAL;
+		vesafb_wait_for_task(tsk);
+		mib = p_mode(tsk->buf);
+		mib->mode_id = *mode;
+
+		/* We only want modes that are supported with the currennt
+		 * hardware configuration (D0), color (D3), graphics (D4)
+		 * and that have support for the LFB (D7). */
+		if ((mib->mode_attr & 0x99) == 0x99 &&
+		    mib->bits_per_pixel >= 8) {
+			off++;
+		} else {
+			vbe_modes_cnt--;
+		}
+		mode++;
+		mib->depth = mib->red_len + mib->green_len + mib->blue_len;
+		/* Handle 8bpp modes and modes with broken color component
+		 * lengths. */
+		if (mib->depth == 0 ||
+		    (mib->depth == 24 && mib->bits_per_pixel == 32))
+			mib->depth = mib->bits_per_pixel;
+	}
+
+	return 0;
+}
+
+static int __init inline vesafb_vbe_getpmi(struct vesafb_task *tsk)
+{
+	int i;
+
+	vesafb_reset_task(tsk);
+	tsk->regs.eax = 0x4f0a;
+	tsk->regs.ebx = 0x0;
+	tsk->flags = TF_CALL;
+	if (vesafb_queue_task(tsk))
+		return -EINVAL;
+	vesafb_wait_for_task(tsk);
+
+	if ((tsk->regs.eax & 0xffff) != 0x004f || tsk->regs.es < 0xc000) {
+		pmi_setpal = ypan = 0;
+	} else {
+		pmi_base  = (u16*)phys_to_virt(((u32)tsk->regs.es << 4) +
+			     tsk->regs.edi);
+		pmi_start = (void*)((char*)pmi_base + pmi_base[1]);
+		pmi_pal   = (void*)((char*)pmi_base + pmi_base[2]);
+		printk(KERN_INFO "vesafb: protected mode interface info at "
+				 "%04x:%04x\n",
+				 (u16)tsk->regs.es, (u16)tsk->regs.edi);
+		printk(KERN_INFO "vesafb: pmi: set display start = %p, "
+				 "set palette = %p\n", pmi_start, pmi_pal);
+
+		if (pmi_base[3]) {
+			printk(KERN_INFO "vesafb: pmi: ports = ");
+			for (i = pmi_base[3]/2; pmi_base[i] != 0xffff; i++)
+				printk("%x ",pmi_base[i]);
+			printk("\n");
+
+			/*
+			 * memory areas not supported (yet?)
+			 *
+			 * Rules are: we have to set up a descriptor for the
+			 * requested memory area and pass it in the ES register
+			 * to the BIOS function.
+			 */
+			if (pmi_base[i] != 0xffff) {
+				printk(KERN_INFO "vesafb: can't handle memory "
+						 "requests, pmi disabled\n");
+				ypan = pmi_setpal = 0;
+			}
+		}
+	}
+	return 0;
+}
+
+static int __init inline vesafb_vbe_getedid(struct vesafb_task *tsk,
+					    struct fb_info *info)
+{
+	int res = 0;
+
+	if (noedid || vbe_ib.vbe_version < 0x0300)
+		return -EINVAL;
+
+	vesafb_reset_task(tsk);
+	tsk->regs.eax = 0x4f15;
+	tsk->regs.ebx = 0;
+	tsk->regs.ecx = 0;
+	if (vesafb_queue_task(tsk))
+		return -EINVAL;
+	vesafb_wait_for_task(tsk);
+
+	if ((tsk->regs.eax & 0xffff) != 0x004f)
+		return -EINVAL;
+
+	if ((tsk->regs.ebx & 0x3) == 3) {
+		printk(KERN_INFO "vesafb: VBIOS/hardware supports both "
+				 "DDC1 and DDC2 transfers\n");
+	} else if ((tsk->regs.ebx & 0x3) == 2) {
+		printk(KERN_INFO "vesafb: VBIOS/hardware supports DDC2 "
+				 "transfers\n");
+	} else if ((tsk->regs.ebx & 0x3) == 1) {
+		printk(KERN_INFO "vesafb: VBIOS/hardware supports DDC1 "
+				 "transfers\n");
+	} else {
+		printk(KERN_INFO "vesafb: VBIOS/hardware doesn't support "
+				 "DDC transfers\n");
+		return -EINVAL;
+	}
+
+	vesafb_reset_task(tsk);
+	tsk->regs.eax = 0x4f15;
+	tsk->regs.ebx = 1;
+	tsk->regs.ecx = tsk->regs.edx = 0;
+	tsk->flags = TF_CALL | TF_RETURN_BUF | TF_BUF_DI;
+	tsk->buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+	tsk->buf_len = EDID_LENGTH;
+
+	if (vesafb_queue_task(tsk)) {
+		res = -EINVAL;
+		goto out;
+	}
+	vesafb_wait_for_task(tsk);
+
+	if ((tsk->regs.eax & 0xffff) == 0x004f) {
+		fb_edid_to_monspecs(tsk->buf, &info->monspecs);
+		fb_videomode_to_modelist(info->monspecs.modedb,
+				info->monspecs.modedb_len, &info->modelist);
+		if (info->monspecs.vfmax && info->monspecs.hfmax) {
+			/* If the maximum pixel clock wasn't specified in
+			 * the EDID block, set it to 300 MHz. */
+			if (info->monspecs.dclkmax == 0)
+				info->monspecs.dclkmax = 300 * 1000000;
+			info->monspecs.gtf = 1;
+		} else {
+			res = -EINVAL;
+		}
+	}
+
+out:	kfree(tsk->buf);
+	return res;
+}
+
+static void __init inline vesafb_vbe_getmonspecs(struct vesafb_task *tsk,
+		                                 struct fb_info *info)
+{
+	struct fb_var_screeninfo var;
+	int i;
+	memset(&info->monspecs, 0, sizeof(struct fb_monspecs));
+
+	/* If we didn't get all necessary data from the EDID block,
+	 * mark it as incompatible with the GTF. */
+	if (vesafb_vbe_getedid(tsk, info))
+		info->monspecs.gtf = 0;
+
+	/* Kernel command line overrides. */
+	if (maxclk)
+		info->monspecs.dclkmax = maxclk * 1000000;
+	if (maxvf)
+		info->monspecs.vfmax = maxvf;
+	if (maxhf)
+		info->monspecs.hfmax = maxhf * 1000;
+
+	/* In case DDC transfers are not supported the user can provide
+	 * monitor limits manually. Lower limits are set to "safe" values. */
+	if (info->monspecs.gtf == 0 && maxclk && maxvf && maxhf) {
+		info->monspecs.dclkmin = 0;
+		info->monspecs.vfmin = 60;
+		info->monspecs.hfmin = 29000;
+		info->monspecs.gtf = 1;
+	}
+
+	if (info->monspecs.gtf) {
+		printk(KERN_INFO
+		       	"vesafb: monitor limits: vf = %d Hz, hf = %d kHz, "
+			"clk = %d MHz\n", info->monspecs.vfmax,
+			(int)(info->monspecs.hfmax / 1000),
+			(int)(info->monspecs.dclkmax / 1000000));
+		/* Add valid VESA video modes to our modelist. */
+		for (i = 0; i < VESA_MODEDB_SIZE; i++) {
+			fb_videomode_to_var(&var, (struct fb_videomode *)
+					    &vesa_modes[i]);
+			if (!fb_validate_mode(&var, info))
+				fb_add_videomode((struct fb_videomode *)
+						 &vesa_modes[i],
+						 &info->modelist);
+		}
+	} else {
+		/* Add all VESA video modes to our modelist. */
+		fb_videomode_to_modelist((struct fb_videomode *)vesa_modes,
+				 	 VESA_MODEDB_SIZE, &info->modelist);
+		printk(KERN_INFO "vesafb: no monitor limits have been set\n");
+	}
+	return;
+}
+
+static int __init inline vesafb_vbe_init(struct fb_info *info)
+{
+	struct vesafb_task *tsk;
+	int res = 0;
+
+	vesafb_create_task(tsk);
+	if (!tsk)
+		return -EINVAL;
+	if ((res = vesafb_vbe_getinfo(tsk)) != 0)
+		goto out;
+	if ((res = vesafb_vbe_getmodes(tsk)) != 0)
+		goto out;
+	if (pmi_setpal || ypan)
+		vesafb_vbe_getpmi(tsk);
+
+	INIT_LIST_HEAD(&info->modelist);
+	vesafb_vbe_getmonspecs(tsk, info);
+
+out:	kfree(tsk);
+	return res;
+}
+
+static int __init decode_mode(u32 *xres, u32 *yres, u32 *bpp, u32 *refresh)
+{
+	int len = strlen(mode_option), i, err = 0;
+	u8 res_specified = 0, bpp_specified = 0, refresh_specified = 0,
+	   yres_specified = 0;
+
+	for (i = len-1; i >= 0; i--) {
+ 		switch (mode_option[i]) {
+		case '@':
+    			len = i;
+    			if (!refresh_specified && !bpp_specified &&
+			    !yres_specified) {
+				*refresh = simple_strtoul(&mode_option[i+1],
+							  NULL, 0);
+				refresh_specified = 1;
+			} else
+				goto out;
+		    	break;
+		case '-':
+			len = i;
+		    	if (!bpp_specified && !yres_specified) {
+			    	*bpp = simple_strtoul(&mode_option[i+1],
+						      NULL, 0);
+				bpp_specified = 1;
+		    	} else
+				goto out;
+		    	break;
+		case 'x':
+			if (!yres_specified) {
+				*yres = simple_strtoul(&mode_option[i+1],
+						       NULL, 0);
+				yres_specified = 1;
+		    	} else
+				goto out;
+		    	break;
+		case '0'...'9':
+			break;
+		default:
+			goto out;
+	    	}
+	}
+
+	if (i < 0 && yres_specified) {
+		*xres = simple_strtoul(mode_option, NULL, 0);
+	   	res_specified = 1;
+	}
+
+out:	if (!res_specified || !yres_specified) {
+		printk(KERN_ERR "vesafb: invalid resolution, "
+				"%s not specified\n",
+				(!res_specified) ? "width" : "height");
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+static int __init vesafb_init_set_mode(struct fb_info *info)
+{
+	struct fb_videomode *fbmode;
+	struct fb_videomode mode;
+	int i, modeid, refresh = 0;
+	u8 refresh_specified = 0;
+
+	if (!mode_option)
+		mode_option = CONFIG_FB_VESA_DEFAULT_MODE;
+
+	if (vbemode > 0) {
+		for (i = 0; i < vbe_modes_cnt; i++) {
+			if (vbe_modes[i].mode_id == vbemode) {
+				info->var.vmode = FB_VMODE_NONINTERLACED;
+				info->var.sync = FB_SYNC_VERT_HIGH_ACT;
+				vesafb_setup_var(&info->var, info,
+						 &vbe_modes[i]);
+				fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
+					    60, &info->var, info);
+				/* With pixclock set to 0, the default BIOS
+				 * timings will be used in set_par(). */
+				info->var.pixclock = 0;
+				modeid = i;
+				goto out;
+			}
+		}
+		printk(KERN_INFO "specified VBE mode %d not found\n",
+				 vbemode);
+		vbemode = 0;
+	}
+
+	/* Decode the mode specified on the kernel command line. We save
+	 * the depth into bits_per_pixel, which is wrong, but will work
+	 * anyway. */
+	if (decode_mode(&info->var.xres, &info->var.yres,
+			&info->var.bits_per_pixel, &refresh))
+		return -EINVAL;
+	if (refresh)
+		refresh_specified = 1;
+	else
+		refresh = 60;
+
+	/* Look for a matching VBE mode. We can live if an exact match
+	 * cannot be found. */
+	modeid = vesafb_find_vbe_mode(info->var.xres, info->var.yres,
+			              info->var.bits_per_pixel, 0);
+
+	if (modeid == -1) {
+		return -EINVAL;
+	} else {
+		info->var.vmode = FB_VMODE_NONINTERLACED;
+		info->var.sync = FB_SYNC_VERT_HIGH_ACT;
+		vesafb_setup_var(&info->var, info, &vbe_modes[modeid]);
+	}
+	if (vbe_ib.vbe_version < 0x0300) {
+		fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60,
+			    &info->var, info);
+		goto out;
+	}
+	if (!gtf) {
+		struct fb_videomode tmode;
+
+		if (refresh_specified) {
+			fb_var_to_videomode(&tmode, &info->var);
+			tmode.refresh = refresh;
+			fbmode = fb_find_nearest_mode(&tmode, 
+						      &info->modelist);
+		} else
+			fbmode = fb_find_best_mode(&info->var, 
+						   &info->modelist);
+
+		if (fbmode->xres == info->var.xres &&
+		    fbmode->yres == info->var.yres &&
+		    !(fbmode->vmode & (FB_VMODE_INTERLACED | FB_VMODE_DOUBLE))
+		    && (!refresh_specified || 
+		    abs(refresh - fbmode->refresh) <= 5)) {
+			fb_videomode_to_var(&info->var, fbmode);
+			return modeid;
+		}
+	}
+	i = FB_MAXTIMINGS;
+	if (!info->monspecs.gtf)
+		i = FB_IGNOREMON | FB_VSYNCTIMINGS;
+	else if (refresh_specified)
+		i = FB_VSYNCTIMINGS;
+	if (!fb_get_mode(i, refresh, &info->var, info))
+		goto out;
+	if (info->monspecs.gtf &&
+	    !fb_get_mode(FB_MAXTIMINGS, 0, &info->var, info))
+		goto out;
+	/* Use default refresh rate */
+	printk(KERN_WARNING "vesafb: using default BIOS refresh rate\n");
+	info->var.pixclock = 0;
+
+out:
+	fb_var_to_videomode(&mode, &info->var);
+	fb_add_videomode(&mode, &info->modelist);
+	return modeid;
+}
+
+static int __init vesafb_probe(struct platform_device *dev)
+{
+	char entry[16];
+	struct fb_info *info;
+	struct vesafb_mode_ib *mode = NULL;
+	int err = 0, i, h;
+	unsigned int size_vmode;
+	unsigned int size_remap;
+	unsigned int size_total;
+
+	vesafb_info = info = framebuffer_alloc(sizeof(struct vesafb_par) +
+			                       sizeof(u32) * 256, &dev->dev);
+	if (!info)
+	 	return -ENOMEM;
+
+	if (vesafb_wait_for_thread()) {
+		printk(KERN_ERR "vesafb: vesafb thread not running\n");
+		framebuffer_release(info);
+		return -EINVAL;
+	}
+
+	if (vesafb_vbe_init(info)) {
+		printk(KERN_ERR "vesafb: vbe_init failed\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	vesafb_fix.ypanstep  = ypan     ? 1 : 0;
+	vesafb_fix.ywrapstep = (ypan>1) ? 1 : 0;
+
+	info->pseudo_palette = ((u8*)info->par + sizeof(struct vesafb_par));
+	info->fbops = &vesafb_ops;
+	info->var = vesafb_defined;
+	info->fix = vesafb_fix;
+
+	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
+		err = -ENXIO;
+		goto out;
+	}
+
+	i = vesafb_init_set_mode(info);
+	if (i < 0) {
+		err = -EINVAL;
+		goto out_cmap;
+	} else
+		mode = &vbe_modes[i];
+
+	/* Disable blanking if the user requested so. */
+	if (!blank) {
+		info->fbops->fb_blank = NULL;
+	}
+
+	/* Find out how much IO memory is required for the mode with
+	 * the highest resolution. */
+	size_remap = 0;
+	for (i = 0; i < vbe_modes_cnt; i++) {
+		h = vbe_modes[i].bytes_per_scan_line * vbe_modes[i].y_res;
+		if (h > size_remap)
+			size_remap = h;
+	}
+	size_remap *= 2;
+
+	/*   size_vmode -- that is the amount of memory needed for the
+	 *                 used video mode, i.e. the minimum amount of
+	 *                 memory we need. */
+	if (mode != NULL) {
+		size_vmode = info->var.yres * mode->bytes_per_scan_line;
+	} else {
+		size_vmode = info->var.yres * info->var.xres *
+			     ((info->var.bits_per_pixel + 7) >> 3);
+	}
+
+	/*   size_total -- all video memory we have. Used for mtrr
+	 *                 entries, ressource allocation and bounds
+	 *                 checking. */
+	size_total = vbe_ib.total_memory * 65536;
+	if (vram_total)
+		size_total = vram_total * 1024 * 1024;
+	if (size_total < size_vmode)
+		size_total = size_vmode;
+	((struct vesafb_par*)(info->par))->mem_total = size_total;
+
+	/*   size_remap -- the amount of video memory we are going to
+	 *                 use for vesafb.  With modern cards it is no
+	 *                 option to simply use size_total as th
+	 *                 wastes plenty of kernel address space. */
+	if (vram_remap)
+		size_remap = vram_remap * 1024 * 1024;
+	if (size_remap < size_vmode)
+		size_remap = size_vmode;
+	if (size_remap > size_total)
+		size_remap = size_total;
+
+	info->fix.smem_len = size_remap;
+	info->fix.smem_start = mode->phys_base_ptr;
+
+	/* We have to set it here, because when setup_var() was called,
+	 * smem_len wasn't defined yet. */
+	info->var.yres_virtual = info->fix.smem_len /
+				 mode->bytes_per_scan_line;
+
+	if (ypan && info->var.yres_virtual > info->var.yres) {
+		printk(KERN_INFO "vesafb: scrolling: %s "
+		       "using protected mode interface, "
+		       "yres_virtual=%d\n",
+		       (ypan > 1) ? "ywrap" : "ypan",info->var.yres_virtual);
+	} else {
+		printk(KERN_INFO "vesafb: scrolling: redraw\n");
+		info->var.yres_virtual = info->var.yres;
+		ypan = 0;
+	}
+
+	info->flags = FBINFO_FLAG_DEFAULT |
+		(ypan) ? FBINFO_HWACCEL_YPAN : 0;
+
+	if (!ypan)
+		info->fbops->fb_pan_display = NULL;
+
+	if (!request_mem_region(info->fix.smem_start, size_total, "vesafb")) {
+		printk(KERN_WARNING "vesafb: cannot reserve video memory at "
+		       "0x%lx\n", info->fix.smem_start);
+		/* We cannot make this fatal. Sometimes this comes from magic
+		   spaces our resource handlers simply don't know about. */
+	}
+
+	info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+
+	if (!info->screen_base) {
+		printk(KERN_ERR
+		       "vesafb: abort, cannot ioremap video memory "
+		       "0x%x @ 0x%lx\n",
+		       info->fix.smem_len, info->fix.smem_start);
+		err = -EIO;
+		goto out_mem;
+ 	}
+
+	/* Request failure does not faze us, as vgacon probably has this
+	   region already (FIXME) */
+	request_region(0x3c0, 32, "vesafb");
+
+#ifdef CONFIG_MTRR
+	if (mtrr && !(info->fix.smem_start & (PAGE_SIZE - 1))) {
+		int temp_size = size_total;
+		unsigned int type = 0;
+
+		switch (mtrr) {
+		case 1:
+			type = MTRR_TYPE_UNCACHABLE;
+			break;
+		case 2:
+			type = MTRR_TYPE_WRBACK;
+			break;
+		case 3:
+			type = MTRR_TYPE_WRCOMB;
+			break;
+		case 4:
+			type = MTRR_TYPE_WRTHROUGH;
+			break;
+		default:
+			type = 0;
+			break;
+		}
+
+		if (type) {
+			int rc;
+
+			/* Find the largest power-of-two */
+			while (temp_size & (temp_size - 1))
+				temp_size &= (temp_size - 1);
+
+			/* Try and find a power of two to add */
+			do {
+				rc = mtrr_add(info->fix.smem_start,
+					      temp_size, type, 1);
+				temp_size >>= 1;
+			} while (temp_size >= PAGE_SIZE && rc == -EINVAL);
+  		}
+  	}
+#endif /* CONFIG_MTRR */
+
+	if (register_framebuffer(info) < 0) {
+		printk(KERN_ERR
+		       "vesafb: failed to register framebuffer device\n");
+		err = -EINVAL;
+		goto out_mem;
+	}
+
+  	printk(KERN_INFO "vesafb: framebuffer at 0x%lx, mapped to 0x%p, "
+	       "using %dk, total %dk\n", info->fix.smem_start,
+	       info->screen_base, size_remap/1024, size_total/1024);
+	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+	       info->fix.id);
+
+	sprintf(entry, "fb%d", info->node);
+	proc_mkdir(entry, 0);
+
+	sprintf(entry, "fb%d/modes", info->node);
+	create_proc_read_entry(entry, 0, 0, vesafb_read_proc_modes, NULL);
+
+	sprintf(entry, "fb%d/vbe_info", info->node);
+	create_proc_read_entry(entry, 0, 0, vesafb_read_proc_vbe_info, NULL);
+	return 0;
+
+out_mem:
+	release_mem_region(info->fix.smem_start, size_total);
+	if (!list_empty(&info->modelist))
+		fb_destroy_modelist(&info->modelist);
+	fb_destroy_modedb(info->monspecs.modedb);
+out_cmap:
+	fb_dealloc_cmap(&info->cmap);
+out:
+	framebuffer_release(info);
+	vesafb_info = NULL;
+	kfree(vbe_modes);
+	vbe_modes = NULL;
+	return err;
+}
+
+int __init vesafb_init(void)
+{
+	int ret;
+#ifndef MODULE
+	char *option = NULL;
+
+	if (fb_get_options("vesafb", &option))
+		return -ENODEV;
+	vesafb_setup(option);
+#endif
+	ret = platform_driver_register(&vesafb_driver);
+
+	if (!ret) {
+		vesafb_device = platform_device_alloc("vesafb", 0);
+
+		if (vesafb_device)
+			ret = platform_device_add(vesafb_device);
+		else
+			ret = -ENOMEM;
+
+		if (ret) {
+			platform_device_put(vesafb_device);
+			platform_driver_unregister(&vesafb_driver);
+		}
+	}
+	return ret;
+}
+
+module_init(vesafb_init);
+
+#ifdef MODULE
+void __exit vesafb_exit(void)
+{
+	char entry[16];
+
+	if (vesafb_info)
+		unregister_framebuffer(vesafb_info);
+
+	platform_device_unregister(vesafb_device);
+	platform_driver_unregister(&vesafb_driver);
+
+	if (vesafb_info) {
+		struct vesafb_par *par = (struct vesafb_par*)vesafb_info->par;
+
+		sprintf(entry, "fb%d/modes", vesafb_info->node);
+		remove_proc_entry(entry, NULL);
+
+		sprintf(entry, "fb%d/vbe_info", vesafb_info->node);
+		remove_proc_entry(entry, NULL);
+
+		sprintf(entry, "fb%d", vesafb_info->node);
+		remove_proc_entry(entry, NULL);
+
+		iounmap(vesafb_info->screen_base);
+		release_mem_region(vesafb_info->fix.smem_start,
+				   par->mem_total);
+		fb_dealloc_cmap(&vesafb_info->cmap);
+		if (!list_empty(&vesafb_info->modelist))
+			fb_destroy_modelist(&vesafb_info->modelist);
+		fb_destroy_modedb(vesafb_info->monspecs.modedb);
+		framebuffer_release(vesafb_info);
+	}
+
+	if (vbe_modes != NULL)
+		kfree(vbe_modes);
+}
+
+module_exit(vesafb_exit);
+
+static inline int param_get_scroll(char *buffer, struct kernel_param *kp)
+{
+	return 0;
+}
+static inline int param_set_scroll(const char *val, struct kernel_param *kp)
+{
+	ypan = 0;
+
+	if (! strcmp(val, "redraw"))
+		ypan = 0;
+	else if (! strcmp(val, "ypan"))
+		ypan = 1;
+	else if (! strcmp(val, "ywrap"))
+		ypan = 2;
+
+	return 0;
+}
+
+#define param_check_scroll(name, p) __param_check(name, p, void);
+
+module_param_named(scroll, ypan, scroll, 0);
+MODULE_PARM_DESC(scroll,"Scrolling mode, set to 'redraw', 'ypan' or 'ywrap'");
+module_param_named(vgapal, pmi_setpal, invbool, 0);
+MODULE_PARM_DESC(vgapal,"bool: set palette using VGA registers");
+module_param_named(pmipal, pmi_setpal, bool, 0);
+MODULE_PARM_DESC(pmipal,"bool: set palette using PMI calls");
+module_param(mtrr, uint, 0);
+MODULE_PARM_DESC(mtrr,"Memory Type Range Registers setting. Use 0 to disable.");
+module_param(blank, bool, 1);
+MODULE_PARM_DESC(blank,"bool: enable hardware blanking");
+module_param(nocrtc, bool, 0);
+MODULE_PARM_DESC(nocrtc,"bool: ignore CRTC timings when setting modes");
+module_param(noedid, bool, 0);
+MODULE_PARM_DESC(noedid,"bool: ignore EDID-provided monitor limits "
+		        "when setting modes");
+module_param(gtf, bool, 0);
+MODULE_PARM_DESC(gtf,"bool: force use of VESA GTF to calculate mode timings");
+module_param(vram_remap, uint, 0);
+MODULE_PARM_DESC(vram_remap,"Set amount of video memory to be used [MiB]");
+module_param(vram_total, uint, 0);
+MODULE_PARM_DESC(vram_total,"Set total amount of video memoery [MiB]");
+module_param(maxclk, ushort, 0);
+MODULE_PARM_DESC(maxclk,"Maximum pixelclock [MHz], overrides EDID data");
+module_param(maxhf, ushort, 0);
+MODULE_PARM_DESC(maxhf,"Maximum horizontal frequency [kHz], "
+		       "overrides EDID data");
+module_param(maxvf, ushort, 0);
+MODULE_PARM_DESC(maxvf,"Maximum vertical frequency [Hz], "
+		       "overrides EDID data");
+module_param_named(mode, mode_option, charp, 0);
+MODULE_PARM_DESC(mode, "Specify resolution as "
+		       "\"<xres>x<yres>[-<bpp>][@<refresh>]\"");
+module_param(vbemode, ushort, 0);
+MODULE_PARM_DESC(vbemode,"VBE mode number to set, overrides 'mode' setting");
+
+#endif /* MODULE */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Januszewski");
+MODULE_DESCRIPTION("Framebuffer driver for VBE2.0+ compliant graphics boards");
+
Index: linux-2.6.22/include/linux/sched.h
===================================================================
--- linux-2.6.22.orig/include/linux/sched.h
+++ linux-2.6.22/include/linux/sched.h
@@ -1585,6 +1585,8 @@ extern void mmput(struct mm_struct *);
 extern struct mm_struct *get_task_mm(struct task_struct *task);
 /* Remove the current tasks stale references to the old mm_struct */
 extern void mm_release(struct task_struct *, struct mm_struct *);
+/* Create a new mm for a kernel thread */
+extern int set_new_mm(void);
 
 extern int  copy_thread(int, unsigned long, unsigned long, unsigned long, struct task_struct *, struct pt_regs *);
 extern void flush_thread(void);
Index: linux-2.6.22/include/video/vesa.h
===================================================================
--- /dev/null
+++ linux-2.6.22/include/video/vesa.h
@@ -0,0 +1,150 @@
+#if 0
+#define DPRINTK(fmt, args...)	printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , \
+						  ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#define p_crtc(arg) ((struct vesafb_crtc_ib*)(arg))
+#define p_vbe(arg)  ((struct vesafb_vbe_ib*)(arg))
+#define p_mode(arg) ((struct vesafb_mode_ib*)(arg))
+
+struct vesafb_task {
+	u8 flags;
+	void *buf;
+	int buf_len;
+	struct vm86_regs regs;
+	struct list_head node;
+	struct completion done;
+};
+
+/* Vesafb task flags and masks */
+#define TF_CALL		0x00
+#define TF_EXIT		0x01
+#define TF_GETVBEIB	0x02
+#define TF_BUF_DI	0x04
+#define TF_BUF_BX	0x08
+#define TF_RETURN_BUF	0x10
+
+/* Macros and functions for manipulating vesafb tasks */
+#define vesafb_create_task(task)				\
+do { 								\
+	task = kmalloc(sizeof(struct vesafb_task), GFP_ATOMIC); \
+	if (task) 						\
+		memset(task, 0, sizeof(struct vesafb_task));	\
+	init_completion(&task->done);				\
+} while (0)
+
+#define vesafb_wait_for_task(task) 	wait_for_completion(&task->done);
+#define vesafb_reset_task(task)		init_completion(&task->done);
+int vesafb_queue_task(struct vesafb_task *task);
+
+/* Functions for controlling the vesafb thread */
+int vesafb_wait_for_thread(void);
+
+#define VBE_CAP_CAN_SWITCH_DAC	0x01
+#define VBE_CAP_VGACOMPAT	0x02
+
+/* This struct is 512 bytes long */
+struct vesafb_vbe_ib {
+	char vbe_signature[4];
+	u16  vbe_version;
+	u32  oem_string_ptr;
+	u32  capabilities;
+	u32  mode_list_ptr;
+	u16  total_memory;
+	u16  oem_software_rev;
+	u32  oem_vendor_name_ptr;
+	u32  oem_product_name_ptr;
+	u32  oem_product_rev_ptr;
+	u8   reserved[222];
+	char oem_data[256];
+} __attribute__ ((packed));
+
+struct vesafb_crtc_ib {
+	u16 horiz_total;
+	u16 horiz_start;
+	u16 horiz_end;
+	u16 vert_total;
+	u16 vert_start;
+	u16 vert_end;
+	u8  flags;
+	u32 pixel_clock;
+	u16 refresh_rate;
+	u8  reserved[40];
+} __attribute__ ((packed));
+
+#define VBE_MODE_VGACOMPAT	0x20
+
+struct vesafb_mode_ib {
+	/* for all VBE revisions */
+	u16 mode_attr;
+	u8  winA_attr;
+	u8  winB_attr;
+	u16 win_granularity;
+	u16 win_size;
+	u16 winA_seg;
+	u16 winB_seg;
+	u32 win_func_ptr;
+	u16 bytes_per_scan_line;
+
+	/* for VBE 1.2+ */
+	u16 x_res;
+	u16 y_res;
+	u8  x_char_size;
+	u8  y_char_size;
+	u8  planes;
+	u8  bits_per_pixel;
+	u8  banks;
+	u8  memory_model;
+	u8  bank_size;
+	u8  image_pages;
+	u8  reserved1;
+
+	/* Direct color fields for direct/6 and YUV/7 memory models. */
+	/* Offsets are bit positions of lsb in the mask. */
+	u8  red_len;
+	u8  red_off;
+	u8  green_len;
+	u8  green_off;
+	u8  blue_len;
+	u8  blue_off;
+	u8  rsvd_len;
+	u8  rsvd_off;
+	u8  direct_color_info;	/* direct color mode attributes */
+
+	/* for VBE 2.0+ */
+	u32 phys_base_ptr;
+	u8  reserved2[6];
+
+	/* for VBE 3.0+ */
+	u16 lin_bytes_per_scan_line;
+	u8  bnk_image_pages;
+	u8  lin_image_pages;
+	u8  lin_red_len;
+	u8  lin_red_off;
+	u8  lin_green_len;
+	u8  lin_green_off;
+	u8  lin_blue_len;
+	u8  lin_blue_off;
+	u8  lin_rsvd_len;
+	u8  lin_rsvd_off;
+	u32 max_pixel_clock;
+	u16 mode_id;
+	u8  depth;
+} __attribute__ ((packed));
+
+struct vesafb_pal_entry {
+	u_char blue, green, red, pad;
+} __attribute__ ((packed));
+
+struct vesafb_par {
+	u8 *vbe_state;
+	int vbe_state_size;
+	atomic_t ref_count;
+	
+	u32 mem_total;
+	int mode_idx;
+	struct vesafb_crtc_ib crtc;
+};
+
Index: linux-2.6.22/kernel/fork.c
===================================================================
--- linux-2.6.22.orig/kernel/fork.c
+++ linux-2.6.22/kernel/fork.c
@@ -100,6 +100,7 @@ struct kmem_cache *fs_cachep;
 
 /* SLAB cache for vm_area_struct structures */
 struct kmem_cache *vm_area_cachep;
+EXPORT_SYMBOL_GPL(vm_area_cachep);
 
 /* SLAB cache for mm_struct structures (tsk->mm) */
 static struct kmem_cache *mm_cachep;
@@ -401,6 +402,40 @@ void mmput(struct mm_struct *mm)
 EXPORT_SYMBOL_GPL(mmput);
 
 /**
+ * set_new_mm - allocate, init and activate a new mm for a kernel thread
+ */
+int set_new_mm(void)
+{
+	struct mm_struct *mm;
+	struct task_struct *tsk = current;
+	struct mm_struct *active_mm;
+
+	mm = mm_alloc();
+	if (!mm)
+		goto fail_nomem;
+	if (init_new_context(current,mm))
+		goto fail_nocontext;
+
+	task_lock(tsk);
+	tsk->flags |= PF_BORROWED_MM;	
+	active_mm = tsk->active_mm;
+	current->mm = mm;
+	current->active_mm = mm;
+	activate_mm(active_mm, mm);
+	task_unlock(current);
+
+	/* Drop the previous active_mm */
+	mmdrop(active_mm);
+	return 0;
+	
+fail_nocontext:
+	mmdrop(mm);
+fail_nomem:
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(set_new_mm);
+
+/**
  * get_task_mm - acquire a reference to the task's mm
  *
  * Returns %NULL if the task has no mm.  Checks PF_BORROWED_MM (meaning
Index: linux-2.6.22/mm/memory.c
===================================================================
--- linux-2.6.22.orig/mm/memory.c
+++ linux-2.6.22/mm/memory.c
@@ -1193,6 +1193,7 @@ int zeromap_page_range(struct vm_area_st
 	} while (pgd++, addr = next, addr != end);
 	return err;
 }
+EXPORT_SYMBOL_GPL(zeromap_page_range);
 
 pte_t * fastcall get_locked_pte(struct mm_struct *mm, unsigned long addr, spinlock_t **ptl)
 {
Index: linux-2.6.22/mm/mmap.c
===================================================================
--- linux-2.6.22.orig/mm/mmap.c
+++ linux-2.6.22/mm/mmap.c
@@ -2030,6 +2030,7 @@ int insert_vm_struct(struct mm_struct * 
 	vma_link(mm, vma, prev, rb_link, rb_parent);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(insert_vm_struct);
 
 /*
  * Copy the vma structure to a new location in the same mm,
Index: linux-2.6.22/fs/Kconfig
===================================================================
--- linux-2.6.22.orig/fs/Kconfig
+++ linux-2.6.22/fs/Kconfig
@@ -1367,6 +1367,71 @@ config CRAMFS
 
 	  If unsure, say N.
 
+config SQUASHFS
+	tristate "SquashFS 3.2 - Squashed file system support"
+	select ZLIB_INFLATE
+	help
+	  Saying Y here includes support for SquashFS 3.2 (a Compressed Read-Only File
+	  System).  Squashfs is a highly compressed read-only filesystem for Linux.
+	  It uses zlib compression to compress both files, inodes and directories.
+	  Inodes in the system are very small and all blocks are packed to minimise
+	  data overhead. Block sizes greater than 4K are supported up to a maximum of 64K.
+	  SquashFS 3.1 supports 64 bit filesystems and files (larger than 4GB), full
+	  uid/gid information, hard links and timestamps.
+
+	  Squashfs is intended for general read-only filesystem use, for archival
+	  use (i.e. in cases where a .tar.gz file may be used), and in embedded
+	  systems where low overhead is needed.  Further information and filesystem tools
+	  are available from http://squashfs.sourceforge.net.
+
+	  If you want to compile this as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/modules.txt>.  The module
+	  will be called squashfs.  Note that the root file system (the one
+	  containing the directory /) cannot be compiled as a module.
+
+	  If unsure, say N.
+
+config SQUASHFS_EMBEDDED
+
+	bool "Additional options for memory-constrained systems" 
+	depends on SQUASHFS
+	default n
+	help
+	  Saying Y here allows you to specify cache sizes and how Squashfs
+	  allocates memory.  This is only intended for memory constrained
+	  systems.
+
+	  If unsure, say N.
+
+config SQUASHFS_FRAGMENT_CACHE_SIZE
+	int "Number of fragments cached" if SQUASHFS_EMBEDDED
+	depends on SQUASHFS
+	default "3"
+	help
+	  By default SquashFS caches the last 3 fragments read from
+	  the filesystem.  Increasing this amount may mean SquashFS
+	  has to re-read fragments less often from disk, at the expense
+	  of extra system memory.  Decreasing this amount will mean
+	  SquashFS uses less memory at the expense of extra reads from disk.
+
+	  Note there must be at least one cached fragment.  Anything
+	  much more than three will probably not make much difference.
+
+config SQUASHFS_VMALLOC
+	bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED
+	depends on SQUASHFS
+	default n
+	help
+	  By default SquashFS uses kmalloc to obtain fragment cache memory.
+	  Kmalloc memory is the standard kernel allocator, but it can fail
+	  on memory constrained systems.  Because of the way Vmalloc works,
+	  Vmalloc can succeed when kmalloc fails.  Specifying this option
+	  will make SquashFS always use Vmalloc to allocate the
+	  fragment cache memory.
+
+	  If unsure, say N.
+
 config VXFS_FS
 	tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
 	depends on BLOCK
Index: linux-2.6.22/fs/Makefile
===================================================================
--- linux-2.6.22.orig/fs/Makefile
+++ linux-2.6.22/fs/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_JBD)		+= jbd/
 obj-$(CONFIG_JBD2)		+= jbd2/
 obj-$(CONFIG_EXT2_FS)		+= ext2/
 obj-$(CONFIG_CRAMFS)		+= cramfs/
+obj-$(CONFIG_SQUASHFS)		+= squashfs/
 obj-$(CONFIG_RAMFS)		+= ramfs/
 obj-$(CONFIG_HUGETLBFS)		+= hugetlbfs/
 obj-$(CONFIG_CODA_FS)		+= coda/
Index: linux-2.6.22/fs/squashfs/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.22/fs/squashfs/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the linux squashfs routines.
+#
+
+obj-$(CONFIG_SQUASHFS) += squashfs.o
+squashfs-y += inode.o
+squashfs-y += squashfs2_0.o
Index: linux-2.6.22/fs/squashfs/inode.c
===================================================================
--- /dev/null
+++ linux-2.6.22/fs/squashfs/inode.c
@@ -0,0 +1,2328 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
+ * Phillip Lougher <phillip@lougher.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * inode.c
+ */
+
+#include <linux/squashfs_fs.h>
+#include <linux/module.h>
+#include <linux/zlib.h>
+#include <linux/fs.h>
+#include <linux/squashfs_fs_sb.h>
+#include <linux/squashfs_fs_i.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+
+#include "squashfs.h"
+
+static void vfs_read_inode(struct inode *i);
+static struct dentry *squashfs_get_parent(struct dentry *child);
+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode);
+static int squashfs_statfs(struct dentry *, struct kstatfs *);
+static int squashfs_symlink_readpage(struct file *file, struct page *page);
+static long long read_blocklist(struct inode *inode, int index,
+				int readahead_blks, char *block_list,
+				unsigned short **block_p, unsigned int *bsize);
+static int squashfs_readpage(struct file *file, struct page *page);
+static int squashfs_readpage4K(struct file *file, struct page *page);
+static int squashfs_readdir(struct file *, void *, filldir_t);
+static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
+				struct nameidata *);
+static int squashfs_remount(struct super_block *s, int *flags, char *data);
+static void squashfs_put_super(struct super_block *);
+static int squashfs_get_sb(struct file_system_type *,int, const char *, void *,
+				struct vfsmount *);
+static struct inode *squashfs_alloc_inode(struct super_block *sb);
+static void squashfs_destroy_inode(struct inode *inode);
+static int init_inodecache(void);
+static void destroy_inodecache(void);
+
+static struct file_system_type squashfs_fs_type = {
+	.owner = THIS_MODULE,
+	.name = "squashfs",
+	.get_sb = squashfs_get_sb,
+	.kill_sb = kill_block_super,
+	.fs_flags = FS_REQUIRES_DEV
+};
+
+static const unsigned char squashfs_filetype_table[] = {
+	DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+static struct super_operations squashfs_super_ops = {
+	.alloc_inode = squashfs_alloc_inode,
+	.destroy_inode = squashfs_destroy_inode,
+	.statfs = squashfs_statfs,
+	.put_super = squashfs_put_super,
+	.remount_fs = squashfs_remount
+};
+
+static struct super_operations squashfs_export_super_ops = {
+	.alloc_inode = squashfs_alloc_inode,
+	.destroy_inode = squashfs_destroy_inode,
+	.statfs = squashfs_statfs,
+	.put_super = squashfs_put_super,
+	.read_inode = vfs_read_inode
+};
+
+static struct export_operations squashfs_export_ops = {
+	.get_parent = squashfs_get_parent
+};
+
+SQSH_EXTERN const struct address_space_operations squashfs_symlink_aops = {
+	.readpage = squashfs_symlink_readpage
+};
+
+SQSH_EXTERN const struct address_space_operations squashfs_aops = {
+	.readpage = squashfs_readpage
+};
+
+SQSH_EXTERN const struct address_space_operations squashfs_aops_4K = {
+	.readpage = squashfs_readpage4K
+};
+
+static const struct file_operations squashfs_dir_ops = {
+	.read = generic_read_dir,
+	.readdir = squashfs_readdir
+};
+
+SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = {
+	.lookup = squashfs_lookup
+};
+
+
+static struct buffer_head *get_block_length(struct super_block *s,
+				int *cur_index, int *offset, int *c_byte)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	unsigned short temp;
+	struct buffer_head *bh;
+
+	if (!(bh = sb_bread(s, *cur_index)))
+		goto out;
+
+	if (msblk->devblksize - *offset == 1) {
+		if (msblk->swap)
+			((unsigned char *) &temp)[1] = *((unsigned char *)
+				(bh->b_data + *offset));
+		else
+			((unsigned char *) &temp)[0] = *((unsigned char *)
+				(bh->b_data + *offset));
+		brelse(bh);
+		if (!(bh = sb_bread(s, ++(*cur_index))))
+			goto out;
+		if (msblk->swap)
+			((unsigned char *) &temp)[0] = *((unsigned char *)
+				bh->b_data); 
+		else
+			((unsigned char *) &temp)[1] = *((unsigned char *)
+				bh->b_data); 
+		*c_byte = temp;
+		*offset = 1;
+	} else {
+		if (msblk->swap) {
+			((unsigned char *) &temp)[1] = *((unsigned char *)
+				(bh->b_data + *offset));
+			((unsigned char *) &temp)[0] = *((unsigned char *)
+				(bh->b_data + *offset + 1)); 
+		} else {
+			((unsigned char *) &temp)[0] = *((unsigned char *)
+				(bh->b_data + *offset));
+			((unsigned char *) &temp)[1] = *((unsigned char *)
+				(bh->b_data + *offset + 1)); 
+		}
+		*c_byte = temp;
+		*offset += 2;
+	}
+
+	if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
+		if (*offset == msblk->devblksize) {
+			brelse(bh);
+			if (!(bh = sb_bread(s, ++(*cur_index))))
+				goto out;
+			*offset = 0;
+		}
+		if (*((unsigned char *) (bh->b_data + *offset)) !=
+						SQUASHFS_MARKER_BYTE) {
+			ERROR("Metadata block marker corrupt @ %x\n",
+						*cur_index);
+			brelse(bh);
+			goto out;
+		}
+		(*offset)++;
+	}
+	return bh;
+
+out:
+	return NULL;
+}
+
+
+SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
+			long long index, unsigned int length,
+			long long *next_index, int srclength)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
+			msblk->devblksize_log2) + 2];
+	unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
+	unsigned int cur_index = index >> msblk->devblksize_log2;
+	int bytes, avail_bytes, b = 0, k = 0;
+	unsigned int compressed;
+	unsigned int c_byte = length;
+
+	if (c_byte) {
+		bytes = msblk->devblksize - offset;
+		compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
+		c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
+
+		TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed
+					? "" : "un", (unsigned int) c_byte, srclength);
+
+		if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used)
+			goto read_failure;
+
+		if (!(bh[0] = sb_getblk(s, cur_index)))
+			goto block_release;
+
+		for (b = 1; bytes < c_byte; b++) {
+			if (!(bh[b] = sb_getblk(s, ++cur_index)))
+				goto block_release;
+			bytes += msblk->devblksize;
+		}
+		ll_rw_block(READ, b, bh);
+	} else {
+		if (index < 0 || (index + 2) > sblk->bytes_used)
+			goto read_failure;
+
+		if (!(bh[0] = get_block_length(s, &cur_index, &offset,
+								&c_byte)))
+			goto read_failure;
+
+		bytes = msblk->devblksize - offset;
+		compressed = SQUASHFS_COMPRESSED(c_byte);
+		c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
+
+		TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
+					? "" : "un", (unsigned int) c_byte);
+
+		if (c_byte > srclength || (index + c_byte) > sblk->bytes_used)
+			goto read_failure;
+
+		for (b = 1; bytes < c_byte; b++) {
+			if (!(bh[b] = sb_getblk(s, ++cur_index)))
+				goto block_release;
+			bytes += msblk->devblksize;
+		}
+		ll_rw_block(READ, b - 1, bh + 1);
+	}
+
+	if (compressed) {
+		int zlib_err = 0;
+
+		/*
+	 	* uncompress block
+	 	*/
+
+		mutex_lock(&msblk->read_data_mutex);
+
+		msblk->stream.next_out = buffer;
+		msblk->stream.avail_out = srclength;
+
+		for (bytes = 0; k < b; k++) {
+			avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
+					msblk->devblksize - offset :
+					c_byte - bytes;
+			wait_on_buffer(bh[k]);
+			if (!buffer_uptodate(bh[k]))
+				goto release_mutex;
+
+			msblk->stream.next_in = bh[k]->b_data + offset;
+			msblk->stream.avail_in = avail_bytes;
+
+			if (k == 0) {
+				zlib_err = zlib_inflateInit(&msblk->stream);
+				if (zlib_err != Z_OK) {
+					ERROR("zlib_inflateInit returned unexpected result 0x%x, srclength %d\n",
+						zlib_err, srclength);
+					goto release_mutex;
+				}
+
+				if (avail_bytes == 0) {
+					offset = 0;
+					brelse(bh[k]);
+					continue;
+				}
+			}
+
+			zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
+			if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) {
+				ERROR("zlib_inflate returned unexpected result 0x%x, srclength %d, avail_in %d, avail_out %d\n",
+					zlib_err, srclength, msblk->stream.avail_in, msblk->stream.avail_out);
+				goto release_mutex;
+			}
+
+			bytes += avail_bytes;
+			offset = 0;
+			brelse(bh[k]);
+		}
+
+		if (zlib_err != Z_STREAM_END)
+			goto release_mutex;
+
+		zlib_err = zlib_inflateEnd(&msblk->stream);
+		if (zlib_err != Z_OK) {
+			ERROR("zlib_inflateEnd returned unexpected result 0x%x, srclength %d\n",
+				zlib_err, srclength);
+			goto release_mutex;
+		}
+		bytes = msblk->stream.total_out;
+		mutex_unlock(&msblk->read_data_mutex);
+	} else {
+		int i;
+
+		for(i = 0; i < b; i++) {
+			wait_on_buffer(bh[i]);
+			if(!buffer_uptodate(bh[i]))
+				goto block_release;
+		}
+
+		for (bytes = 0; k < b; k++) {
+			avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
+					msblk->devblksize - offset :
+					c_byte - bytes;
+			memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes);
+			bytes += avail_bytes;
+			offset = 0;
+			brelse(bh[k]);
+		}
+	}
+
+	if (next_index)
+		*next_index = index + c_byte + (length ? 0 :
+				(SQUASHFS_CHECK_DATA(msblk->sblk.flags)
+				 ? 3 : 2));
+	return bytes;
+
+release_mutex:
+	mutex_unlock(&msblk->read_data_mutex);
+
+block_release:
+	for (; k < b; k++)
+		brelse(bh[k]);
+
+read_failure:
+	ERROR("sb_bread failed reading block 0x%x\n", cur_index);
+	return 0;
+}
+
+
+SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
+				long long block, unsigned int offset,
+				int length, long long *next_block,
+				unsigned int *next_offset)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	int n, i, bytes, return_length = length;
+	long long next_index;
+
+	TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
+
+	while ( 1 ) {
+		for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) 
+			if (msblk->block_cache[i].block == block)
+				break; 
+		
+		mutex_lock(&msblk->block_cache_mutex);
+
+		if (i == SQUASHFS_CACHED_BLKS) {
+			/* read inode header block */
+			for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS;
+					n ; n --, i = (i + 1) %
+					SQUASHFS_CACHED_BLKS)
+				if (msblk->block_cache[i].block !=
+							SQUASHFS_USED_BLK)
+					break;
+
+			if (n == 0) {
+				wait_queue_t wait;
+
+				init_waitqueue_entry(&wait, current);
+				add_wait_queue(&msblk->waitq, &wait);
+				set_current_state(TASK_UNINTERRUPTIBLE);
+ 				mutex_unlock(&msblk->block_cache_mutex);
+				schedule();
+				set_current_state(TASK_RUNNING);
+				remove_wait_queue(&msblk->waitq, &wait);
+				continue;
+			}
+			msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
+
+			if (msblk->block_cache[i].block ==
+							SQUASHFS_INVALID_BLK) {
+				if (!(msblk->block_cache[i].data =
+						kmalloc(SQUASHFS_METADATA_SIZE,
+						GFP_KERNEL))) {
+					ERROR("Failed to allocate cache"
+							"block\n");
+					mutex_unlock(&msblk->block_cache_mutex);
+					goto out;
+				}
+			}
+	
+			msblk->block_cache[i].block = SQUASHFS_USED_BLK;
+			mutex_unlock(&msblk->block_cache_mutex);
+
+			msblk->block_cache[i].length = squashfs_read_data(s,
+				msblk->block_cache[i].data, block, 0, &next_index, SQUASHFS_METADATA_SIZE);
+			if (msblk->block_cache[i].length == 0) {
+				ERROR("Unable to read cache block [%llx:%x]\n",
+						block, offset);
+				mutex_lock(&msblk->block_cache_mutex);
+				msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
+				kfree(msblk->block_cache[i].data);
+				wake_up(&msblk->waitq);
+				mutex_unlock(&msblk->block_cache_mutex);
+				goto out;
+			}
+
+			mutex_lock(&msblk->block_cache_mutex);
+			wake_up(&msblk->waitq);
+			msblk->block_cache[i].block = block;
+			msblk->block_cache[i].next_index = next_index;
+			TRACE("Read cache block [%llx:%x]\n", block, offset);
+		}
+
+		if (msblk->block_cache[i].block != block) {
+			mutex_unlock(&msblk->block_cache_mutex);
+			continue;
+		}
+
+		bytes = msblk->block_cache[i].length - offset;
+
+		if (bytes < 1) {
+			mutex_unlock(&msblk->block_cache_mutex);
+			goto out;
+		} else if (bytes >= length) {
+			if (buffer)
+				memcpy(buffer, msblk->block_cache[i].data +
+						offset, length);
+			if (msblk->block_cache[i].length - offset == length) {
+				*next_block = msblk->block_cache[i].next_index;
+				*next_offset = 0;
+			} else {
+				*next_block = block;
+				*next_offset = offset + length;
+			}
+			mutex_unlock(&msblk->block_cache_mutex);
+			goto finish;
+		} else {
+			if (buffer) {
+				memcpy(buffer, msblk->block_cache[i].data +
+						offset, bytes);
+				buffer += bytes;
+			}
+			block = msblk->block_cache[i].next_index;
+			mutex_unlock(&msblk->block_cache_mutex);
+			length -= bytes;
+			offset = 0;
+		}
+	}
+
+finish:
+	return return_length;
+out:
+	return 0;
+}
+
+
+static int get_fragment_location(struct super_block *s, unsigned int fragment,
+				long long *fragment_start_block,
+				unsigned int *fragment_size)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	long long start_block =
+		msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
+	int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
+	struct squashfs_fragment_entry fragment_entry;
+
+	if (msblk->swap) {
+		struct squashfs_fragment_entry sfragment_entry;
+
+		if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
+					start_block, offset,
+					sizeof(sfragment_entry), &start_block,
+					&offset))
+			goto out;
+		SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry);
+	} else
+		if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
+					start_block, offset,
+					sizeof(fragment_entry), &start_block,
+					&offset))
+			goto out;
+
+	*fragment_start_block = fragment_entry.start_block;
+	*fragment_size = fragment_entry.size;
+
+	return 1;
+
+out:
+	return 0;
+}
+
+
+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct
+					squashfs_fragment_cache *fragment)
+{
+	mutex_lock(&msblk->fragment_mutex);
+	fragment->locked --;
+	wake_up(&msblk->fragment_wait_queue);
+	mutex_unlock(&msblk->fragment_mutex);
+}
+
+
+SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block
+					*s, long long start_block,
+					int length)
+{
+	int i, n;
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+
+	while ( 1 ) {
+		mutex_lock(&msblk->fragment_mutex);
+
+		for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
+				msblk->fragment[i].block != start_block; i++);
+
+		if (i == SQUASHFS_CACHED_FRAGMENTS) {
+			for (i = msblk->next_fragment, n =
+				SQUASHFS_CACHED_FRAGMENTS; n &&
+				msblk->fragment[i].locked; n--, i = (i + 1) %
+				SQUASHFS_CACHED_FRAGMENTS);
+
+			if (n == 0) {
+				wait_queue_t wait;
+
+				init_waitqueue_entry(&wait, current);
+				add_wait_queue(&msblk->fragment_wait_queue,
+									&wait);
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				mutex_unlock(&msblk->fragment_mutex);
+				schedule();
+				set_current_state(TASK_RUNNING);
+				remove_wait_queue(&msblk->fragment_wait_queue,
+									&wait);
+				continue;
+			}
+			msblk->next_fragment = (msblk->next_fragment + 1) %
+				SQUASHFS_CACHED_FRAGMENTS;
+			
+			if (msblk->fragment[i].data == NULL)
+				if (!(msblk->fragment[i].data = SQUASHFS_ALLOC
+						(SQUASHFS_FILE_MAX_SIZE))) {
+					ERROR("Failed to allocate fragment "
+							"cache block\n");
+					mutex_unlock(&msblk->fragment_mutex);
+					goto out;
+				}
+
+			msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
+			msblk->fragment[i].locked = 1;
+			mutex_unlock(&msblk->fragment_mutex);
+
+			if (!(msblk->fragment[i].length = squashfs_read_data(s,
+						msblk->fragment[i].data,
+						start_block, length, NULL, sblk->block_size))) {
+				ERROR("Unable to read fragment cache block "
+							"[%llx]\n", start_block);
+				msblk->fragment[i].locked = 0;
+				smp_mb();
+				goto out;
+			}
+
+			mutex_lock(&msblk->fragment_mutex);
+			msblk->fragment[i].block = start_block;
+			TRACE("New fragment %d, start block %lld, locked %d\n",
+						i, msblk->fragment[i].block,
+						msblk->fragment[i].locked);
+			mutex_unlock(&msblk->fragment_mutex);
+			break;
+		}
+
+		msblk->fragment[i].locked++;
+		mutex_unlock(&msblk->fragment_mutex);
+		TRACE("Got fragment %d, start block %lld, locked %d\n", i,
+						msblk->fragment[i].block,
+						msblk->fragment[i].locked);
+		break;
+	}
+
+	return &msblk->fragment[i];
+
+out:
+	return NULL;
+}
+
+
+static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
+		struct squashfs_base_inode_header *inodeb)
+{
+	i->i_ino = inodeb->inode_number;
+	i->i_mtime.tv_sec = inodeb->mtime;
+	i->i_atime.tv_sec = inodeb->mtime;
+	i->i_ctime.tv_sec = inodeb->mtime;
+	i->i_uid = msblk->uid[inodeb->uid];
+	i->i_mode = inodeb->mode;
+	i->i_size = 0;
+	if (inodeb->guid == SQUASHFS_GUIDS)
+		i->i_gid = i->i_uid;
+	else
+		i->i_gid = msblk->guid[inodeb->guid];
+}
+
+
+static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)];
+	int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1);
+	squashfs_inode_t inode;
+
+	TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino);
+
+	if (msblk->swap) {
+		squashfs_inode_t sinode;
+
+		if (!squashfs_get_cached_block(s, (char *) &sinode, start, offset,
+					sizeof(sinode), &start, &offset))
+			goto out;
+		SQUASHFS_SWAP_INODE_T((&inode), &sinode);
+	} else if (!squashfs_get_cached_block(s, (char *) &inode, start, offset,
+					sizeof(inode), &start, &offset))
+			goto out;
+
+	TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode);
+
+	return inode;
+
+out:
+	return SQUASHFS_INVALID_BLK;
+}
+	
+
+static void vfs_read_inode(struct inode *i)
+{
+	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+	squashfs_inode_t inode = squashfs_inode_lookup(i->i_sb, i->i_ino);
+
+	TRACE("Entered vfs_read_inode\n");
+
+	if(inode != SQUASHFS_INVALID_BLK)
+		(msblk->read_inode)(i, inode);
+}
+
+
+static struct dentry *squashfs_get_parent(struct dentry *child)
+{
+	struct inode *i = child->d_inode;
+	struct inode *parent = iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode);
+	struct dentry *rv;
+
+	TRACE("Entered squashfs_get_parent\n");
+
+	if(parent == NULL) {
+		rv = ERR_PTR(-EACCES);
+		goto out;
+	}
+
+	rv = d_alloc_anon(parent);
+	if(rv == NULL)
+		rv = ERR_PTR(-ENOMEM);
+
+out:
+	return rv;
+}
+
+	
+SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct inode *i = iget_locked(s, inode_number);
+
+	TRACE("Entered squashfs_iget\n");
+
+	if(i && (i->i_state & I_NEW)) {
+		(msblk->read_inode)(i, inode);
+		unlock_new_inode(i);
+	}
+
+	return i;
+}
+
+
+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode)
+{
+	struct super_block *s = i->i_sb;
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	long long block = SQUASHFS_INODE_BLK(inode) +
+		sblk->inode_table_start;
+	unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
+	long long next_block;
+	unsigned int next_offset;
+	union squashfs_inode_header id, sid;
+	struct squashfs_base_inode_header *inodeb = &id.base,
+					  *sinodeb = &sid.base;
+
+	TRACE("Entered squashfs_read_inode\n");
+
+	if (msblk->swap) {
+		if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
+					offset, sizeof(*sinodeb), &next_block,
+					&next_offset))
+			goto failed_read;
+		SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb,
+					sizeof(*sinodeb));
+	} else
+		if (!squashfs_get_cached_block(s, (char *) inodeb, block,
+					offset, sizeof(*inodeb), &next_block,
+					&next_offset))
+			goto failed_read;
+
+	squashfs_new_inode(msblk, i, inodeb);
+
+	switch(inodeb->inode_type) {
+		case SQUASHFS_FILE_TYPE: {
+			unsigned int frag_size;
+			long long frag_blk;
+			struct squashfs_reg_inode_header *inodep = &id.reg;
+			struct squashfs_reg_inode_header *sinodep = &sid.reg;
+				
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			frag_blk = SQUASHFS_INVALID_BLK;
+			if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
+					!get_fragment_location(s,
+					inodep->fragment, &frag_blk, &frag_size))
+				goto failed_read;
+				
+			i->i_nlink = 1;
+			i->i_size = inodep->file_size;
+			i->i_fop = &generic_ro_fops;
+			i->i_mode |= S_IFREG;
+			i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+			SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+			SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+			SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+			SQUASHFS_I(i)->start_block = inodep->start_block;
+			SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+			SQUASHFS_I(i)->offset = next_offset;
+			if (sblk->block_size > 4096)
+				i->i_data.a_ops = &squashfs_aops;
+			else
+				i->i_data.a_ops = &squashfs_aops_4K;
+
+			TRACE("File inode %x:%x, start_block %llx, "
+					"block_list_start %llx, offset %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					inodep->start_block, next_block,
+					next_offset);
+			break;
+		}
+		case SQUASHFS_LREG_TYPE: {
+			unsigned int frag_size;
+			long long frag_blk;
+			struct squashfs_lreg_inode_header *inodep = &id.lreg;
+			struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
+				
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			frag_blk = SQUASHFS_INVALID_BLK;
+			if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
+					!get_fragment_location(s,
+					inodep->fragment, &frag_blk, &frag_size))
+				goto failed_read;
+				
+			i->i_nlink = inodep->nlink;
+			i->i_size = inodep->file_size;
+			i->i_fop = &generic_ro_fops;
+			i->i_mode |= S_IFREG;
+			i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+			SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+			SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+			SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+			SQUASHFS_I(i)->start_block = inodep->start_block;
+			SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+			SQUASHFS_I(i)->offset = next_offset;
+			if (sblk->block_size > 4096)
+				i->i_data.a_ops = &squashfs_aops;
+			else
+				i->i_data.a_ops = &squashfs_aops_4K;
+
+			TRACE("File inode %x:%x, start_block %llx, "
+					"block_list_start %llx, offset %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					inodep->start_block, next_block,
+					next_offset);
+			break;
+		}
+		case SQUASHFS_DIR_TYPE: {
+			struct squashfs_dir_inode_header *inodep = &id.dir;
+			struct squashfs_dir_inode_header *sinodep = &sid.dir;
+
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_nlink = inodep->nlink;
+			i->i_size = inodep->file_size;
+			i->i_op = &squashfs_dir_inode_ops;
+			i->i_fop = &squashfs_dir_ops;
+			i->i_mode |= S_IFDIR;
+			SQUASHFS_I(i)->start_block = inodep->start_block;
+			SQUASHFS_I(i)->offset = inodep->offset;
+			SQUASHFS_I(i)->u.s2.directory_index_count = 0;
+			SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
+
+			TRACE("Directory inode %x:%x, start_block %x, offset "
+					"%x\n", SQUASHFS_INODE_BLK(inode),
+					offset, inodep->start_block,
+					inodep->offset);
+			break;
+		}
+		case SQUASHFS_LDIR_TYPE: {
+			struct squashfs_ldir_inode_header *inodep = &id.ldir;
+			struct squashfs_ldir_inode_header *sinodep = &sid.ldir;
+
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep,
+						sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_nlink = inodep->nlink;
+			i->i_size = inodep->file_size;
+			i->i_op = &squashfs_dir_inode_ops;
+			i->i_fop = &squashfs_dir_ops;
+			i->i_mode |= S_IFDIR;
+			SQUASHFS_I(i)->start_block = inodep->start_block;
+			SQUASHFS_I(i)->offset = inodep->offset;
+			SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
+			SQUASHFS_I(i)->u.s2.directory_index_offset =
+								next_offset;
+			SQUASHFS_I(i)->u.s2.directory_index_count =
+								inodep->i_count;
+			SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
+
+			TRACE("Long directory inode %x:%x, start_block %x, "
+					"offset %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					inodep->start_block, inodep->offset);
+			break;
+		}
+		case SQUASHFS_SYMLINK_TYPE: {
+			struct squashfs_symlink_inode_header *inodep =
+								&id.symlink;
+			struct squashfs_symlink_inode_header *sinodep =
+								&sid.symlink;
+	
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep,
+								sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_nlink = inodep->nlink;
+			i->i_size = inodep->symlink_size;
+			i->i_op = &page_symlink_inode_operations;
+			i->i_data.a_ops = &squashfs_symlink_aops;
+			i->i_mode |= S_IFLNK;
+			SQUASHFS_I(i)->start_block = next_block;
+			SQUASHFS_I(i)->offset = next_offset;
+
+			TRACE("Symbolic link inode %x:%x, start_block %llx, "
+					"offset %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					next_block, next_offset);
+			break;
+		 }
+		 case SQUASHFS_BLKDEV_TYPE:
+		 case SQUASHFS_CHRDEV_TYPE: {
+			struct squashfs_dev_inode_header *inodep = &id.dev;
+			struct squashfs_dev_inode_header *sinodep = &sid.dev;
+
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep);
+			} else	
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_nlink = inodep->nlink;
+			i->i_mode |= (inodeb->inode_type ==
+					SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
+					S_IFBLK;
+			init_special_inode(i, i->i_mode,
+					old_decode_dev(inodep->rdev));
+
+			TRACE("Device inode %x:%x, rdev %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					inodep->rdev);
+			break;
+		 }
+		 case SQUASHFS_FIFO_TYPE:
+		 case SQUASHFS_SOCKET_TYPE: {
+			struct squashfs_ipc_inode_header *inodep = &id.ipc;
+			struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
+
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep);
+			} else	
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_nlink = inodep->nlink;
+			i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
+							? S_IFIFO : S_IFSOCK;
+			init_special_inode(i, i->i_mode, 0);
+			break;
+		 }
+		 default:
+			ERROR("Unknown inode type %d in squashfs_iget!\n",
+					inodeb->inode_type);
+			goto failed_read1;
+	}
+	
+	return 1;
+
+failed_read:
+	ERROR("Unable to read inode [%llx:%x]\n", block, offset);
+
+failed_read1:
+	make_bad_inode(i);
+	return 0;
+}
+
+
+static int read_inode_lookup_table(struct super_block *s)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes);
+
+	TRACE("In read_inode_lookup_table, length %d\n", length);
+
+	/* Allocate inode lookup table */
+	if (!(msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL))) {
+		ERROR("Failed to allocate inode lookup table\n");
+		return 0;
+	}
+   
+	if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table,
+			sblk->lookup_table_start, length |
+			SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
+		ERROR("unable to read inode lookup table\n");
+		return 0;
+	}
+
+	if (msblk->swap) {
+		int i;
+		long long block;
+
+		for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) {
+			SQUASHFS_SWAP_LOOKUP_BLOCKS((&block),
+						&msblk->inode_lookup_table[i], 1);
+			msblk->inode_lookup_table[i] = block;
+		}
+	}
+
+	return 1;
+}
+
+
+static int read_fragment_index_table(struct super_block *s)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments);
+
+	if(length == 0)
+		return 1;
+
+	/* Allocate fragment index table */
+	if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) {
+		ERROR("Failed to allocate fragment index table\n");
+		return 0;
+	}
+   
+	if (!squashfs_read_data(s, (char *) msblk->fragment_index,
+			sblk->fragment_table_start, length |
+			SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
+		ERROR("unable to read fragment index table\n");
+		return 0;
+	}
+
+	if (msblk->swap) {
+		int i;
+		long long fragment;
+
+		for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) {
+			SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
+						&msblk->fragment_index[i], 1);
+			msblk->fragment_index[i] = fragment;
+		}
+	}
+
+	return 1;
+}
+
+
+static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
+{
+	struct squashfs_super_block *sblk = &msblk->sblk;
+
+	msblk->read_inode = squashfs_read_inode;
+	msblk->read_blocklist = read_blocklist;
+	msblk->read_fragment_index_table = read_fragment_index_table;
+
+	if (sblk->s_major == 1) {
+		if (!squashfs_1_0_supported(msblk)) {
+			SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
+				"are unsupported\n");
+			SERROR("Please recompile with "
+				"Squashfs 1.0 support enabled\n");
+			return 0;
+		}
+	} else if (sblk->s_major == 2) {
+		if (!squashfs_2_0_supported(msblk)) {
+			SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
+				"are unsupported\n");
+			SERROR("Please recompile with "
+				"Squashfs 2.0 support enabled\n");
+			return 0;
+		}
+	} else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
+			SQUASHFS_MINOR) {
+		SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
+				"filesystem\n", sblk->s_major, sblk->s_minor);
+		SERROR("Please update your kernel\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int squashfs_fill_super(struct super_block *s, void *data, int silent)
+{
+	struct squashfs_sb_info *msblk;
+	struct squashfs_super_block *sblk;
+	int i;
+	char b[BDEVNAME_SIZE];
+	struct inode *root;
+
+	TRACE("Entered squashfs_read_superblock\n");
+
+	if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info),
+						GFP_KERNEL))) {
+		ERROR("Failed to allocate superblock\n");
+		goto failure;
+	}
+	memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info));
+	msblk = s->s_fs_info;
+	if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
+		ERROR("Failed to allocate zlib workspace\n");
+		goto failure;
+	}
+	sblk = &msblk->sblk;
+	
+	msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE);
+	msblk->devblksize_log2 = ffz(~msblk->devblksize);
+
+	mutex_init(&msblk->read_data_mutex);
+	mutex_init(&msblk->read_page_mutex);
+	mutex_init(&msblk->block_cache_mutex);
+	mutex_init(&msblk->fragment_mutex);
+	mutex_init(&msblk->meta_index_mutex);
+	
+	init_waitqueue_head(&msblk->waitq);
+	init_waitqueue_head(&msblk->fragment_wait_queue);
+
+	sblk->bytes_used = sizeof(struct squashfs_super_block);
+	if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
+					sizeof(struct squashfs_super_block) |
+					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) {
+		SERROR("unable to read superblock\n");
+		goto failed_mount;
+	}
+
+	/* Check it is a SQUASHFS superblock */
+	msblk->swap = 0;
+	if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
+		if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
+			struct squashfs_super_block ssblk;
+
+			WARNING("Mounting a different endian SQUASHFS "
+				"filesystem on %s\n", bdevname(s->s_bdev, b));
+
+			SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
+			memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
+			msblk->swap = 1;
+		} else  {
+			SERROR("Can't find a SQUASHFS superblock on %s\n",
+							bdevname(s->s_bdev, b));
+			goto failed_mount;
+		}
+	}
+
+	/* Check the MAJOR & MINOR versions */
+	if(!supported_squashfs_filesystem(msblk, silent))
+		goto failed_mount;
+
+	/* Check the filesystem does not extend beyond the end of the
+	   block device */
+	if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode))
+		goto failed_mount;
+
+	/* Check the root inode for sanity */
+	if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE)
+		goto failed_mount;
+
+	TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
+	TRACE("Inodes are %scompressed\n",
+					SQUASHFS_UNCOMPRESSED_INODES
+					(sblk->flags) ? "un" : "");
+	TRACE("Data is %scompressed\n",
+					SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
+					? "un" : "");
+	TRACE("Check data is %s present in the filesystem\n",
+					SQUASHFS_CHECK_DATA(sblk->flags) ?
+					"" : "not");
+	TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
+	TRACE("Block size %d\n", sblk->block_size);
+	TRACE("Number of inodes %d\n", sblk->inodes);
+	if (sblk->s_major > 1)
+		TRACE("Number of fragments %d\n", sblk->fragments);
+	TRACE("Number of uids %d\n", sblk->no_uids);
+	TRACE("Number of gids %d\n", sblk->no_guids);
+	TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
+	TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
+	if (sblk->s_major > 1)
+		TRACE("sblk->fragment_table_start %llx\n",
+					sblk->fragment_table_start);
+	TRACE("sblk->uid_start %llx\n", sblk->uid_start);
+
+	s->s_flags |= MS_RDONLY;
+	s->s_op = &squashfs_super_ops;
+
+	/* Init inode_table block pointer array */
+	if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
+					SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
+		ERROR("Failed to allocate block cache\n");
+		goto failed_mount;
+	}
+
+	for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
+		msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
+
+	msblk->next_cache = 0;
+
+	/* Allocate read_page block */
+	if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) {
+		ERROR("Failed to allocate read_page block\n");
+		goto failed_mount;
+	}
+
+	/* Allocate uid and gid tables */
+	if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
+					sizeof(unsigned int), GFP_KERNEL))) {
+		ERROR("Failed to allocate uid/gid table\n");
+		goto failed_mount;
+	}
+	msblk->guid = msblk->uid + sblk->no_uids;
+   
+	if (msblk->swap) {
+		unsigned int suid[sblk->no_uids + sblk->no_guids];
+
+		if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
+					((sblk->no_uids + sblk->no_guids) *
+					 sizeof(unsigned int)) |
+					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
+			ERROR("unable to read uid/gid table\n");
+			goto failed_mount;
+		}
+
+		SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
+			sblk->no_guids), (sizeof(unsigned int) * 8));
+	} else
+		if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
+					((sblk->no_uids + sblk->no_guids) *
+					 sizeof(unsigned int)) |
+					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
+			ERROR("unable to read uid/gid table\n");
+			goto failed_mount;
+		}
+
+
+	if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
+		goto allocate_root;
+
+	if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) *
+				SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) {
+		ERROR("Failed to allocate fragment block cache\n");
+		goto failed_mount;
+	}
+
+	for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
+		msblk->fragment[i].locked = 0;
+		msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
+		msblk->fragment[i].data = NULL;
+	}
+
+	msblk->next_fragment = 0;
+
+	/* Allocate and read fragment index table */
+	if (msblk->read_fragment_index_table(s) == 0)
+		goto failed_mount;
+
+	if(sblk->s_major < 3 || sblk->lookup_table_start == SQUASHFS_INVALID_BLK)
+		goto allocate_root;
+
+	/* Allocate and read inode lookup table */
+	if (read_inode_lookup_table(s) == 0)
+		goto failed_mount;
+
+	s->s_op = &squashfs_export_super_ops;
+	s->s_export_op = &squashfs_export_ops;
+
+allocate_root:
+	root = new_inode(s);
+	if ((msblk->read_inode)(root, sblk->root_inode) == 0)
+		goto failed_mount;
+	insert_inode_hash(root);
+
+	if ((s->s_root = d_alloc_root(root)) == NULL) {
+		ERROR("Root inode create failed\n");
+		iput(root);
+		goto failed_mount;
+	}
+
+	TRACE("Leaving squashfs_read_super\n");
+	return 0;
+
+failed_mount:
+	kfree(msblk->inode_lookup_table);
+	kfree(msblk->fragment_index);
+	kfree(msblk->fragment);
+	kfree(msblk->uid);
+	kfree(msblk->read_page);
+	kfree(msblk->block_cache);
+	kfree(msblk->fragment_index_2);
+	vfree(msblk->stream.workspace);
+	kfree(s->s_fs_info);
+	s->s_fs_info = NULL;
+	return -EINVAL;
+
+failure:
+	return -ENOMEM;
+}
+
+
+static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+	struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+
+	TRACE("Entered squashfs_statfs\n");
+
+	buf->f_type = SQUASHFS_MAGIC;
+	buf->f_bsize = sblk->block_size;
+	buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
+	buf->f_bfree = buf->f_bavail = 0;
+	buf->f_files = sblk->inodes;
+	buf->f_ffree = 0;
+	buf->f_namelen = SQUASHFS_NAME_LEN;
+
+	return 0;
+}
+
+
+static int squashfs_symlink_readpage(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
+	long long block = SQUASHFS_I(inode)->start_block;
+	int offset = SQUASHFS_I(inode)->offset;
+	void *pageaddr = kmap(page);
+
+	TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
+				"%llx, offset %x\n", page->index,
+				SQUASHFS_I(inode)->start_block,
+				SQUASHFS_I(inode)->offset);
+
+	for (length = 0; length < index; length += bytes) {
+		if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL,
+				block, offset, PAGE_CACHE_SIZE, &block,
+				&offset))) {
+			ERROR("Unable to read symbolic link [%llx:%x]\n", block,
+					offset);
+			goto skip_read;
+		}
+	}
+
+	if (length != index) {
+		ERROR("(squashfs_symlink_readpage) length != index\n");
+		bytes = 0;
+		goto skip_read;
+	}
+
+	bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
+					i_size_read(inode) - length;
+
+	if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block,
+					offset, bytes, &block, &offset)))
+		ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
+
+skip_read:
+	memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+	kunmap(page);
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	unlock_page(page);
+
+	return 0;
+}
+
+
+struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
+{
+	struct meta_index *meta = NULL;
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	int i;
+
+	mutex_lock(&msblk->meta_index_mutex);
+
+	TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
+
+	if(msblk->meta_index == NULL)
+		goto not_allocated;
+
+	for (i = 0; i < SQUASHFS_META_NUMBER; i ++)
+		if (msblk->meta_index[i].inode_number == inode->i_ino &&
+				msblk->meta_index[i].offset >= offset &&
+				msblk->meta_index[i].offset <= index &&
+				msblk->meta_index[i].locked == 0) {
+			TRACE("locate_meta_index: entry %d, offset %d\n", i,
+					msblk->meta_index[i].offset);
+			meta = &msblk->meta_index[i];
+			offset = meta->offset;
+		}
+
+	if (meta)
+		meta->locked = 1;
+
+not_allocated:
+	mutex_unlock(&msblk->meta_index_mutex);
+
+	return meta;
+}
+
+
+struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
+{
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	struct meta_index *meta = NULL;
+	int i;
+
+	mutex_lock(&msblk->meta_index_mutex);
+
+	TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
+
+	if(msblk->meta_index == NULL) {
+		if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) *
+					SQUASHFS_META_NUMBER, GFP_KERNEL))) {
+			ERROR("Failed to allocate meta_index\n");
+			goto failed;
+		}
+		for(i = 0; i < SQUASHFS_META_NUMBER; i++) {
+			msblk->meta_index[i].inode_number = 0;
+			msblk->meta_index[i].locked = 0;
+		}
+		msblk->next_meta_index = 0;
+	}
+
+	for(i = SQUASHFS_META_NUMBER; i &&
+			msblk->meta_index[msblk->next_meta_index].locked; i --)
+		msblk->next_meta_index = (msblk->next_meta_index + 1) %
+			SQUASHFS_META_NUMBER;
+
+	if(i == 0) {
+		TRACE("empty_meta_index: failed!\n");
+		goto failed;
+	}
+
+	TRACE("empty_meta_index: returned meta entry %d, %p\n",
+			msblk->next_meta_index,
+			&msblk->meta_index[msblk->next_meta_index]);
+
+	meta = &msblk->meta_index[msblk->next_meta_index];
+	msblk->next_meta_index = (msblk->next_meta_index + 1) %
+			SQUASHFS_META_NUMBER;
+
+	meta->inode_number = inode->i_ino;
+	meta->offset = offset;
+	meta->skip = skip;
+	meta->entries = 0;
+	meta->locked = 1;
+
+failed:
+	mutex_unlock(&msblk->meta_index_mutex);
+	return meta;
+}
+
+
+void release_meta_index(struct inode *inode, struct meta_index *meta)
+{
+	meta->locked = 0;
+	smp_mb();
+}
+
+
+static int read_block_index(struct super_block *s, int blocks, char *block_list,
+		long long *start_block, int *offset)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	unsigned int *block_listp;
+	int block = 0;
+	
+	if (msblk->swap) {
+		char sblock_list[blocks << 2];
+
+		if (!squashfs_get_cached_block(s, sblock_list, *start_block,
+				*offset, blocks << 2, start_block, offset)) {
+			ERROR("Unable to read block list [%llx:%x]\n",
+				*start_block, *offset);
+			goto failure;
+		}
+		SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
+				((unsigned int *)sblock_list), blocks);
+	} else
+		if (!squashfs_get_cached_block(s, block_list, *start_block,
+				*offset, blocks << 2, start_block, offset)) {
+			ERROR("Unable to read block list [%llx:%x]\n",
+				*start_block, *offset);
+			goto failure;
+		}
+
+	for (block_listp = (unsigned int *) block_list; blocks;
+				block_listp++, blocks --)
+		block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
+
+	return block;
+
+failure:
+	return -1;
+}
+
+
+#define SIZE 256
+
+static inline int calculate_skip(int blocks) {
+	int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
+	return skip >= 7 ? 7 : skip + 1;
+}
+
+
+static int get_meta_index(struct inode *inode, int index,
+		long long *index_block, int *index_offset,
+		long long *data_block, char *block_list)
+{
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
+	int offset = 0;
+	struct meta_index *meta;
+	struct meta_entry *meta_entry;
+	long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
+	int cur_offset = SQUASHFS_I(inode)->offset;
+	long long cur_data_block = SQUASHFS_I(inode)->start_block;
+	int i;
+ 
+	index /= SQUASHFS_META_INDEXES * skip;
+
+	while ( offset < index ) {
+		meta = locate_meta_index(inode, index, offset + 1);
+
+		if (meta == NULL) {
+			if ((meta = empty_meta_index(inode, offset + 1,
+							skip)) == NULL)
+				goto all_done;
+		} else {
+			if(meta->entries == 0)
+				goto failed;
+			offset = index < meta->offset + meta->entries ? index :
+				meta->offset + meta->entries - 1;
+			meta_entry = &meta->meta_entry[offset - meta->offset];
+			cur_index_block = meta_entry->index_block + sblk->inode_table_start;
+			cur_offset = meta_entry->offset;
+			cur_data_block = meta_entry->data_block;
+			TRACE("get_meta_index: offset %d, meta->offset %d, "
+				"meta->entries %d\n", offset, meta->offset,
+				meta->entries);
+			TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
+				" data_block 0x%llx\n", cur_index_block,
+				cur_offset, cur_data_block);
+		}
+
+		for (i = meta->offset + meta->entries; i <= index &&
+				i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
+			int blocks = skip * SQUASHFS_META_INDEXES;
+
+			while (blocks) {
+				int block = blocks > (SIZE >> 2) ? (SIZE >> 2) :
+					blocks;
+				int res = read_block_index(inode->i_sb, block,
+					block_list, &cur_index_block,
+					&cur_offset);
+
+				if (res == -1)
+					goto failed;
+
+				cur_data_block += res;
+				blocks -= block;
+			}
+
+			meta_entry = &meta->meta_entry[i - meta->offset];
+			meta_entry->index_block = cur_index_block - sblk->inode_table_start;
+			meta_entry->offset = cur_offset;
+			meta_entry->data_block = cur_data_block;
+			meta->entries ++;
+			offset ++;
+		}
+
+		TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
+				meta->offset, meta->entries);
+
+		release_meta_index(inode, meta);
+	}
+
+all_done:
+	*index_block = cur_index_block;
+	*index_offset = cur_offset;
+	*data_block = cur_data_block;
+
+	return offset * SQUASHFS_META_INDEXES * skip;
+
+failed:
+	release_meta_index(inode, meta);
+	return -1;
+}
+
+
+static long long read_blocklist(struct inode *inode, int index,
+				int readahead_blks, char *block_list,
+				unsigned short **block_p, unsigned int *bsize)
+{
+	long long block_ptr;
+	int offset;
+	long long block;
+	int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
+		block_list);
+
+	TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
+		       " 0x%x, block 0x%llx\n", res, index, block_ptr, offset,
+		       block);
+
+	if(res == -1)
+		goto failure;
+
+	index -= res;
+
+	while ( index ) {
+		int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
+		int res = read_block_index(inode->i_sb, blocks, block_list,
+			&block_ptr, &offset);
+		if (res == -1)
+			goto failure;
+		block += res;
+		index -= blocks;
+	}
+
+	if (read_block_index(inode->i_sb, 1, block_list,
+			&block_ptr, &offset) == -1)
+		goto failure;
+	*bsize = *((unsigned int *) block_list);
+
+	return block;
+
+failure:
+	return 0;
+}
+
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	unsigned char *block_list;
+	long long block;
+	unsigned int bsize, i = 0, bytes = 0, byte_offset = 0;
+	int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
+ 	void *pageaddr;
+	struct squashfs_fragment_cache *fragment = NULL;
+	char *data_ptr = msblk->read_page;
+	
+	int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+	int start_index = page->index & ~mask;
+	int end_index = start_index | mask;
+
+	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+					page->index,
+					SQUASHFS_I(inode)->start_block);
+
+	if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
+		ERROR("Failed to allocate block_list\n");
+		goto skip_read;
+	}
+
+	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+					PAGE_CACHE_SHIFT))
+		goto skip_read;
+
+	if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+					|| index < (i_size_read(inode) >>
+					sblk->block_log)) {
+		if ((block = (msblk->read_blocklist)(inode, index, 1,
+					block_list, NULL, &bsize)) == 0)
+			goto skip_read;
+
+		mutex_lock(&msblk->read_page_mutex);
+		
+		if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
+					block, bsize, NULL, sblk->block_size))) {
+			ERROR("Unable to read page, block %llx, size %x\n", block,
+					bsize);
+			mutex_unlock(&msblk->read_page_mutex);
+			goto skip_read;
+		}
+	} else {
+		if ((fragment = get_cached_fragment(inode->i_sb,
+					SQUASHFS_I(inode)->
+					u.s1.fragment_start_block,
+					SQUASHFS_I(inode)->u.s1.fragment_size))
+					== NULL) {
+			ERROR("Unable to read page, block %llx, size %x\n",
+					SQUASHFS_I(inode)->
+					u.s1.fragment_start_block,
+					(int) SQUASHFS_I(inode)->
+					u.s1.fragment_size);
+			goto skip_read;
+		}
+		bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
+					(i_size_read(inode) & (sblk->block_size
+					- 1));
+		byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
+		data_ptr = fragment->data;
+	}
+
+	for (i = start_index; i <= end_index && byte_offset < bytes;
+					i++, byte_offset += PAGE_CACHE_SIZE) {
+		struct page *push_page;
+		int avail = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
+					PAGE_CACHE_SIZE : bytes - byte_offset;
+
+		TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
+					bytes, i, byte_offset, avail);
+
+		push_page = (i == page->index) ? page :
+			grab_cache_page_nowait(page->mapping, i);
+
+		if (!push_page)
+			continue;
+
+		if (PageUptodate(push_page))
+			goto skip_page;
+
+ 		pageaddr = kmap_atomic(push_page, KM_USER0);
+		memcpy(pageaddr, data_ptr + byte_offset, avail);
+		memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
+		kunmap_atomic(pageaddr, KM_USER0);
+		flush_dcache_page(push_page);
+		SetPageUptodate(push_page);
+skip_page:
+		unlock_page(push_page);
+		if(i != page->index)
+			page_cache_release(push_page);
+	}
+
+	if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+					|| index < (i_size_read(inode) >>
+					sblk->block_log))
+		mutex_unlock(&msblk->read_page_mutex);
+	else
+		release_cached_fragment(msblk, fragment);
+
+	kfree(block_list);
+	return 0;
+
+skip_read:
+	pageaddr = kmap_atomic(page, KM_USER0);
+	memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+	kunmap_atomic(pageaddr, KM_USER0);
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	unlock_page(page);
+
+	kfree(block_list);
+	return 0;
+}
+
+
+static int squashfs_readpage4K(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	unsigned char *block_list;
+	long long block;
+	unsigned int bsize, bytes = 0;
+ 	void *pageaddr;
+	
+	TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n",
+					page->index,
+					SQUASHFS_I(inode)->start_block);
+
+	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+					PAGE_CACHE_SHIFT)) {
+		block_list = NULL;
+		goto skip_read;
+	}
+
+	if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
+		ERROR("Failed to allocate block_list\n");
+		goto skip_read;
+	}
+
+	if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+					|| page->index < (i_size_read(inode) >>
+					sblk->block_log)) {
+		block = (msblk->read_blocklist)(inode, page->index, 1,
+					block_list, NULL, &bsize);
+		if(block == 0)
+			goto skip_read;
+
+		mutex_lock(&msblk->read_page_mutex);
+		bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
+					bsize, NULL, sblk->block_size);
+		if (bytes) {
+			pageaddr = kmap_atomic(page, KM_USER0);
+			memcpy(pageaddr, msblk->read_page, bytes);
+			kunmap_atomic(pageaddr, KM_USER0);
+		} else
+			ERROR("Unable to read page, block %llx, size %x\n",
+					block, bsize);
+		mutex_unlock(&msblk->read_page_mutex);
+	} else {
+		struct squashfs_fragment_cache *fragment =
+			get_cached_fragment(inode->i_sb,
+					SQUASHFS_I(inode)->
+					u.s1.fragment_start_block,
+					SQUASHFS_I(inode)-> u.s1.fragment_size);
+		if (fragment) {
+			bytes = i_size_read(inode) & (sblk->block_size - 1);
+			pageaddr = kmap_atomic(page, KM_USER0);
+			memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
+					u.s1.fragment_offset, bytes);
+			kunmap_atomic(pageaddr, KM_USER0);
+			release_cached_fragment(msblk, fragment);
+		} else
+			ERROR("Unable to read page, block %llx, size %x\n",
+					SQUASHFS_I(inode)->
+					u.s1.fragment_start_block, (int)
+					SQUASHFS_I(inode)-> u.s1.fragment_size);
+	}
+
+skip_read:
+	pageaddr = kmap_atomic(page, KM_USER0);
+	memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+	kunmap_atomic(pageaddr, KM_USER0);
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	unlock_page(page);
+
+	kfree(block_list);
+	return 0;
+}
+
+
+static int get_dir_index_using_offset(struct super_block *s, long long 
+				*next_block, unsigned int *next_offset,
+				long long index_start,
+				unsigned int index_offset, int i_count,
+				long long f_pos)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	int i, length = 0;
+	struct squashfs_dir_index index;
+
+	TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
+					i_count, (unsigned int) f_pos);
+
+	f_pos =- 3;
+	if (f_pos == 0)
+		goto finish;
+
+	for (i = 0; i < i_count; i++) {
+		if (msblk->swap) {
+			struct squashfs_dir_index sindex;
+			squashfs_get_cached_block(s, (char *) &sindex,
+					index_start, index_offset,
+					sizeof(sindex), &index_start,
+					&index_offset);
+			SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
+		} else
+			squashfs_get_cached_block(s, (char *) &index,
+					index_start, index_offset,
+					sizeof(index), &index_start,
+					&index_offset);
+
+		if (index.index > f_pos)
+			break;
+
+		squashfs_get_cached_block(s, NULL, index_start, index_offset,
+					index.size + 1, &index_start,
+					&index_offset);
+
+		length = index.index;
+		*next_block = index.start_block + sblk->directory_table_start;
+	}
+
+	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+
+finish:
+	return length + 3;
+}
+
+
+static int get_dir_index_using_name(struct super_block *s, long long
+				*next_block, unsigned int *next_offset,
+				long long index_start,
+				unsigned int index_offset, int i_count,
+				const char *name, int size)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	int i, length = 0;
+	struct squashfs_dir_index *index;
+	char *str;
+
+	TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
+
+	if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
+		(SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
+		ERROR("Failed to allocate squashfs_dir_index\n");
+		goto failure;
+	}
+
+	index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1);
+	strncpy(str, name, size);
+	str[size] = '\0';
+
+	for (i = 0; i < i_count; i++) {
+		if (msblk->swap) {
+			struct squashfs_dir_index sindex;
+			squashfs_get_cached_block(s, (char *) &sindex,
+					index_start, index_offset,
+					sizeof(sindex), &index_start,
+					&index_offset);
+			SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
+		} else
+			squashfs_get_cached_block(s, (char *) index,
+					index_start, index_offset,
+					sizeof(struct squashfs_dir_index),
+					&index_start, &index_offset);
+
+		squashfs_get_cached_block(s, index->name, index_start,
+					index_offset, index->size + 1,
+					&index_start, &index_offset);
+
+		index->name[index->size + 1] = '\0';
+
+		if (strcmp(index->name, str) > 0)
+			break;
+
+		length = index->index;
+		*next_block = index->start_block + sblk->directory_table_start;
+	}
+
+	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+	kfree(str);
+failure:
+	return length + 3;
+}
+
+		
+static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+	struct inode *i = file->f_dentry->d_inode;
+	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	long long next_block = SQUASHFS_I(i)->start_block +
+		sblk->directory_table_start;
+	int next_offset = SQUASHFS_I(i)->offset, length = 0,
+		dir_count;
+	struct squashfs_dir_header dirh;
+	struct squashfs_dir_entry *dire;
+
+	TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
+
+	if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
+		SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
+		ERROR("Failed to allocate squashfs_dir_entry\n");
+		goto finish;
+	}
+
+	while(file->f_pos < 3) {
+		char *name;
+		int size, i_ino;
+
+		if(file->f_pos == 0) {
+			name = ".";
+			size = 1;
+			i_ino = i->i_ino;
+		} else {
+			name = "..";
+			size = 2;
+			i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
+		}
+		TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
+				(unsigned int) dirent, name, size, (int)
+				file->f_pos, i_ino,
+				squashfs_filetype_table[1]);
+
+		if (filldir(dirent, name, size,
+				file->f_pos, i_ino,
+				squashfs_filetype_table[1]) < 0) {
+				TRACE("Filldir returned less than 0\n");
+				goto finish;
+		}
+		file->f_pos += size;
+	}
+
+	length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
+				SQUASHFS_I(i)->u.s2.directory_index_start,
+				SQUASHFS_I(i)->u.s2.directory_index_offset,
+				SQUASHFS_I(i)->u.s2.directory_index_count,
+				file->f_pos);
+
+	while (length < i_size_read(i)) {
+		/* read directory header */
+		if (msblk->swap) {
+			struct squashfs_dir_header sdirh;
+			
+			if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+					next_block, next_offset, sizeof(sdirh),
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += sizeof(sdirh);
+			SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
+		} else {
+			if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+					next_block, next_offset, sizeof(dirh),
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += sizeof(dirh);
+		}
+
+		dir_count = dirh.count + 1;
+		while (dir_count--) {
+			if (msblk->swap) {
+				struct squashfs_dir_entry sdire;
+				if (!squashfs_get_cached_block(i->i_sb, (char *)
+						&sdire, next_block, next_offset,
+						sizeof(sdire), &next_block,
+						&next_offset))
+					goto failed_read;
+				
+				length += sizeof(sdire);
+				SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
+			} else {
+				if (!squashfs_get_cached_block(i->i_sb, (char *)
+						dire, next_block, next_offset,
+						sizeof(*dire), &next_block,
+						&next_offset))
+					goto failed_read;
+
+				length += sizeof(*dire);
+			}
+
+			if (!squashfs_get_cached_block(i->i_sb, dire->name,
+						next_block, next_offset,
+						dire->size + 1, &next_block,
+						&next_offset))
+				goto failed_read;
+
+			length += dire->size + 1;
+
+			if (file->f_pos >= length)
+				continue;
+
+			dire->name[dire->size + 1] = '\0';
+
+			TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n",
+					(unsigned int) dirent, dire->name,
+					dire->size + 1, (int) file->f_pos,
+					dirh.start_block, dire->offset,
+					dirh.inode_number + dire->inode_number,
+					squashfs_filetype_table[dire->type]);
+
+			if (filldir(dirent, dire->name, dire->size + 1,
+					file->f_pos,
+					dirh.inode_number + dire->inode_number,
+					squashfs_filetype_table[dire->type])
+					< 0) {
+				TRACE("Filldir returned less than 0\n");
+				goto finish;
+			}
+			file->f_pos = length;
+		}
+	}
+
+finish:
+	kfree(dire);
+	return 0;
+
+failed_read:
+	ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+		next_offset);
+	kfree(dire);
+	return 0;
+}
+
+
+static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry,
+				struct nameidata *nd)
+{
+	const unsigned char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct inode *inode = NULL;
+	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	long long next_block = SQUASHFS_I(i)->start_block +
+				sblk->directory_table_start;
+	int next_offset = SQUASHFS_I(i)->offset, length = 0,
+				dir_count;
+	struct squashfs_dir_header dirh;
+	struct squashfs_dir_entry *dire;
+
+	TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
+
+	if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
+		SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
+		ERROR("Failed to allocate squashfs_dir_entry\n");
+		goto exit_lookup;
+	}
+
+	if (len > SQUASHFS_NAME_LEN)
+		goto exit_lookup;
+
+	length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
+				SQUASHFS_I(i)->u.s2.directory_index_start,
+				SQUASHFS_I(i)->u.s2.directory_index_offset,
+				SQUASHFS_I(i)->u.s2.directory_index_count, name,
+				len);
+
+	while (length < i_size_read(i)) {
+		/* read directory header */
+		if (msblk->swap) {
+			struct squashfs_dir_header sdirh;
+			if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+					next_block, next_offset, sizeof(sdirh),
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += sizeof(sdirh);
+			SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
+		} else {
+			if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+					next_block, next_offset, sizeof(dirh),
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += sizeof(dirh);
+		}
+
+		dir_count = dirh.count + 1;
+		while (dir_count--) {
+			if (msblk->swap) {
+				struct squashfs_dir_entry sdire;
+				if (!squashfs_get_cached_block(i->i_sb, (char *)
+						&sdire, next_block,next_offset,
+						sizeof(sdire), &next_block,
+						&next_offset))
+					goto failed_read;
+				
+				length += sizeof(sdire);
+				SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
+			} else {
+				if (!squashfs_get_cached_block(i->i_sb, (char *)
+						dire, next_block,next_offset,
+						sizeof(*dire), &next_block,
+						&next_offset))
+					goto failed_read;
+
+				length += sizeof(*dire);
+			}
+
+			if (!squashfs_get_cached_block(i->i_sb, dire->name,
+					next_block, next_offset, dire->size + 1,
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += dire->size + 1;
+
+			if (name[0] < dire->name[0])
+				goto exit_lookup;
+
+			if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) {
+				squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block,
+								dire->offset);
+
+				TRACE("calling squashfs_iget for directory "
+					"entry %s, inode %x:%x, %d\n", name,
+					dirh.start_block, dire->offset,
+					dirh.inode_number + dire->inode_number);
+
+				inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number);
+
+				goto exit_lookup;
+			}
+		}
+	}
+
+exit_lookup:
+	kfree(dire);
+	if (inode)
+		return d_splice_alias(inode, dentry);
+	d_add(dentry, inode);
+	return ERR_PTR(0);
+
+failed_read:
+	ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+		next_offset);
+	goto exit_lookup;
+}
+
+
+static int squashfs_remount(struct super_block *s, int *flags, char *data)
+{
+	*flags |= MS_RDONLY;
+	return 0;
+}
+
+
+static void squashfs_put_super(struct super_block *s)
+{
+	int i;
+
+	if (s->s_fs_info) {
+		struct squashfs_sb_info *sbi = s->s_fs_info;
+		if (sbi->block_cache)
+			for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
+				if (sbi->block_cache[i].block !=
+							SQUASHFS_INVALID_BLK)
+					kfree(sbi->block_cache[i].data);
+		if (sbi->fragment)
+			for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) 
+				SQUASHFS_FREE(sbi->fragment[i].data);
+		kfree(sbi->fragment);
+		kfree(sbi->block_cache);
+		kfree(sbi->read_page);
+		kfree(sbi->uid);
+		kfree(sbi->fragment_index);
+		kfree(sbi->fragment_index_2);
+		kfree(sbi->meta_index);
+		vfree(sbi->stream.workspace);
+		kfree(s->s_fs_info);
+		s->s_fs_info = NULL;
+	}
+}
+
+
+static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
+				const char *dev_name, void *data,
+				struct vfsmount *mnt)
+{
+	return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
+				mnt);
+}
+
+
+static int __init init_squashfs_fs(void)
+{
+	int err = init_inodecache();
+	if (err)
+		goto out;
+
+	printk(KERN_INFO "squashfs: version 3.2-r2 (2007/01/15) "
+		"Phillip Lougher\n");
+
+	if ((err = register_filesystem(&squashfs_fs_type)))
+		destroy_inodecache();
+
+out:
+	return err;
+}
+
+
+static void __exit exit_squashfs_fs(void)
+{
+	unregister_filesystem(&squashfs_fs_type);
+	destroy_inodecache();
+}
+
+
+static struct kmem_cache * squashfs_inode_cachep;
+
+
+static struct inode *squashfs_alloc_inode(struct super_block *sb)
+{
+	struct squashfs_inode_info *ei;
+	ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+
+static void squashfs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode));
+}
+
+
+static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
+{
+	struct squashfs_inode_info *ei = foo;
+
+	inode_init_once(&ei->vfs_inode);
+}
+ 
+
+static int __init init_inodecache(void)
+{
+	squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
+	     sizeof(struct squashfs_inode_info),
+	     0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
+	     init_once, NULL);
+	if (squashfs_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+
+static void destroy_inodecache(void)
+{
+	kmem_cache_destroy(squashfs_inode_cachep);
+}
+
+
+module_init(init_squashfs_fs);
+module_exit(exit_squashfs_fs);
+MODULE_DESCRIPTION("squashfs 3.2-r2, a compressed read-only filesystem");
+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>");
+MODULE_LICENSE("GPL");
Index: linux-2.6.22/fs/squashfs/squashfs.h
===================================================================
--- /dev/null
+++ linux-2.6.22/fs/squashfs/squashfs.h
@@ -0,0 +1,87 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
+ * Phillip Lougher <phillip@lougher.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs.h
+ */
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+#endif
+
+#ifdef SQUASHFS_TRACE
+#define TRACE(s, args...)	printk(KERN_NOTICE "SQUASHFS: "s, ## args)
+#else
+#define TRACE(s, args...)	{}
+#endif
+
+#define ERROR(s, args...)	printk(KERN_ERR "SQUASHFS error: "s, ## args)
+
+#define SERROR(s, args...)	do { \
+				if (!silent) \
+				printk(KERN_ERR "SQUASHFS error: "s, ## args);\
+				} while(0)
+
+#define WARNING(s, args...)	printk(KERN_WARNING "SQUASHFS: "s, ## args)
+
+static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode)
+{
+	return list_entry(inode, struct squashfs_inode_info, vfs_inode);
+}
+
+#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
+#define SQSH_EXTERN
+extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
+				long long index, unsigned int length,
+				long long *next_index, int srclength);
+extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
+				long long block, unsigned int offset,
+				int length, long long *next_block,
+				unsigned int *next_offset);
+extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
+					squashfs_fragment_cache *fragment);
+extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
+					*s, long long start_block,
+					int length);
+extern struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number);
+extern const struct address_space_operations squashfs_symlink_aops;
+extern const struct address_space_operations squashfs_aops;
+extern const struct address_space_operations squashfs_aops_4K;
+extern struct inode_operations squashfs_dir_inode_ops;
+#else
+#define SQSH_EXTERN static
+#endif
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
+#else
+static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
+{
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
+#else
+static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
+{
+	return 0;
+}
+#endif
Index: linux-2.6.22/fs/squashfs/squashfs2_0.c
===================================================================
--- /dev/null
+++ linux-2.6.22/fs/squashfs/squashfs2_0.c
@@ -0,0 +1,742 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
+ * Phillip Lougher <phillip@lougher.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs2_0.c
+ */
+
+#include <linux/squashfs_fs.h>
+#include <linux/module.h>
+#include <linux/zlib.h>
+#include <linux/fs.h>
+#include <linux/squashfs_fs_sb.h>
+#include <linux/squashfs_fs_i.h>
+
+#include "squashfs.h"
+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
+static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *,
+				struct nameidata *);
+
+static struct file_operations squashfs_dir_ops_2 = {
+	.read = generic_read_dir,
+	.readdir = squashfs_readdir_2
+};
+
+static struct inode_operations squashfs_dir_inode_ops_2 = {
+	.lookup = squashfs_lookup_2
+};
+
+static unsigned char squashfs_filetype_table[] = {
+	DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+static int read_fragment_index_table_2(struct super_block *s)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+
+	if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
+					(sblk->fragments), GFP_KERNEL))) {
+		ERROR("Failed to allocate uid/gid table\n");
+		return 0;
+	}
+   
+	if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
+					!squashfs_read_data(s, (char *)
+					msblk->fragment_index_2,
+					sblk->fragment_table_start,
+					SQUASHFS_FRAGMENT_INDEX_BYTES_2
+					(sblk->fragments) |
+					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
+		ERROR("unable to read fragment index table\n");
+		return 0;
+	}
+
+	if (msblk->swap) {
+		int i;
+		unsigned int fragment;
+
+		for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
+									i++) {
+			SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
+						&msblk->fragment_index_2[i], 1);
+			msblk->fragment_index_2[i] = fragment;
+		}
+	}
+
+	return 1;
+}
+
+
+static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
+				long long *fragment_start_block,
+				unsigned int *fragment_size)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	long long start_block =
+		msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
+	int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
+	struct squashfs_fragment_entry_2 fragment_entry;
+
+	if (msblk->swap) {
+		struct squashfs_fragment_entry_2 sfragment_entry;
+
+		if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
+					start_block, offset,
+					sizeof(sfragment_entry), &start_block,
+					&offset))
+			goto out;
+		SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
+	} else
+		if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
+					start_block, offset,
+					sizeof(fragment_entry), &start_block,
+					&offset))
+			goto out;
+
+	*fragment_start_block = fragment_entry.start_block;
+	*fragment_size = fragment_entry.size;
+
+	return 1;
+
+out:
+	return 0;
+}
+
+
+static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
+		struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
+{
+	struct squashfs_super_block *sblk = &msblk->sblk;
+
+	i->i_ino = ino;
+	i->i_mtime.tv_sec = sblk->mkfs_time;
+	i->i_atime.tv_sec = sblk->mkfs_time;
+	i->i_ctime.tv_sec = sblk->mkfs_time;
+	i->i_uid = msblk->uid[inodeb->uid];
+	i->i_mode = inodeb->mode;
+	i->i_nlink = 1;
+	i->i_size = 0;
+	if (inodeb->guid == SQUASHFS_GUIDS)
+		i->i_gid = i->i_uid;
+	else
+		i->i_gid = msblk->guid[inodeb->guid];
+}
+
+
+static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
+{
+	struct super_block *s = i->i_sb;
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	unsigned int block = SQUASHFS_INODE_BLK(inode) +
+		sblk->inode_table_start;
+	unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
+	unsigned int ino = i->i_ino;
+	long long next_block;
+	unsigned int next_offset;
+	union squashfs_inode_header_2 id, sid;
+	struct squashfs_base_inode_header_2 *inodeb = &id.base,
+					  *sinodeb = &sid.base;
+
+	TRACE("Entered squashfs_iget\n");
+
+	if (msblk->swap) {
+		if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
+					offset, sizeof(*sinodeb), &next_block,
+					&next_offset))
+			goto failed_read;
+		SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
+					sizeof(*sinodeb));
+	} else
+		if (!squashfs_get_cached_block(s, (char *) inodeb, block,
+					offset, sizeof(*inodeb), &next_block,
+					&next_offset))
+			goto failed_read;
+
+	squashfs_new_inode(msblk, i, inodeb, ino);
+
+	switch(inodeb->inode_type) {
+		case SQUASHFS_FILE_TYPE: {
+			struct squashfs_reg_inode_header_2 *inodep = &id.reg;
+			struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
+			long long frag_blk;
+			unsigned int frag_size = 0;
+				
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			frag_blk = SQUASHFS_INVALID_BLK;
+			if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
+					!get_fragment_location_2(s,
+					inodep->fragment, &frag_blk, &frag_size))
+				goto failed_read;
+				
+			i->i_size = inodep->file_size;
+			i->i_fop = &generic_ro_fops;
+			i->i_mode |= S_IFREG;
+			i->i_mtime.tv_sec = inodep->mtime;
+			i->i_atime.tv_sec = inodep->mtime;
+			i->i_ctime.tv_sec = inodep->mtime;
+			i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+			SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+			SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+			SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+			SQUASHFS_I(i)->start_block = inodep->start_block;
+			SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+			SQUASHFS_I(i)->offset = next_offset;
+			if (sblk->block_size > 4096)
+				i->i_data.a_ops = &squashfs_aops;
+			else
+				i->i_data.a_ops = &squashfs_aops_4K;
+
+			TRACE("File inode %x:%x, start_block %x, "
+					"block_list_start %llx, offset %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					inodep->start_block, next_block,
+					next_offset);
+			break;
+		}
+		case SQUASHFS_DIR_TYPE: {
+			struct squashfs_dir_inode_header_2 *inodep = &id.dir;
+			struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
+
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_size = inodep->file_size;
+			i->i_op = &squashfs_dir_inode_ops_2;
+			i->i_fop = &squashfs_dir_ops_2;
+			i->i_mode |= S_IFDIR;
+			i->i_mtime.tv_sec = inodep->mtime;
+			i->i_atime.tv_sec = inodep->mtime;
+			i->i_ctime.tv_sec = inodep->mtime;
+			SQUASHFS_I(i)->start_block = inodep->start_block;
+			SQUASHFS_I(i)->offset = inodep->offset;
+			SQUASHFS_I(i)->u.s2.directory_index_count = 0;
+			SQUASHFS_I(i)->u.s2.parent_inode = 0;
+
+			TRACE("Directory inode %x:%x, start_block %x, offset "
+					"%x\n", SQUASHFS_INODE_BLK(inode),
+					offset, inodep->start_block,
+					inodep->offset);
+			break;
+		}
+		case SQUASHFS_LDIR_TYPE: {
+			struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
+			struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
+
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
+						sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_size = inodep->file_size;
+			i->i_op = &squashfs_dir_inode_ops_2;
+			i->i_fop = &squashfs_dir_ops_2;
+			i->i_mode |= S_IFDIR;
+			i->i_mtime.tv_sec = inodep->mtime;
+			i->i_atime.tv_sec = inodep->mtime;
+			i->i_ctime.tv_sec = inodep->mtime;
+			SQUASHFS_I(i)->start_block = inodep->start_block;
+			SQUASHFS_I(i)->offset = inodep->offset;
+			SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
+			SQUASHFS_I(i)->u.s2.directory_index_offset =
+								next_offset;
+			SQUASHFS_I(i)->u.s2.directory_index_count =
+								inodep->i_count;
+			SQUASHFS_I(i)->u.s2.parent_inode = 0;
+
+			TRACE("Long directory inode %x:%x, start_block %x, "
+					"offset %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					inodep->start_block, inodep->offset);
+			break;
+		}
+		case SQUASHFS_SYMLINK_TYPE: {
+			struct squashfs_symlink_inode_header_2 *inodep =
+								&id.symlink;
+			struct squashfs_symlink_inode_header_2 *sinodep =
+								&sid.symlink;
+	
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
+								sinodep);
+			} else
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_size = inodep->symlink_size;
+			i->i_op = &page_symlink_inode_operations;
+			i->i_data.a_ops = &squashfs_symlink_aops;
+			i->i_mode |= S_IFLNK;
+			SQUASHFS_I(i)->start_block = next_block;
+			SQUASHFS_I(i)->offset = next_offset;
+
+			TRACE("Symbolic link inode %x:%x, start_block %llx, "
+					"offset %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					next_block, next_offset);
+			break;
+		 }
+		 case SQUASHFS_BLKDEV_TYPE:
+		 case SQUASHFS_CHRDEV_TYPE: {
+			struct squashfs_dev_inode_header_2 *inodep = &id.dev;
+			struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
+
+			if (msblk->swap) {
+				if (!squashfs_get_cached_block(s, (char *)
+						sinodep, block, offset,
+						sizeof(*sinodep), &next_block,
+						&next_offset))
+					goto failed_read;
+				SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
+			} else	
+				if (!squashfs_get_cached_block(s, (char *)
+						inodep, block, offset,
+						sizeof(*inodep), &next_block,
+						&next_offset))
+					goto failed_read;
+
+			i->i_mode |= (inodeb->inode_type ==
+					SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
+					S_IFBLK;
+			init_special_inode(i, i->i_mode,
+					old_decode_dev(inodep->rdev));
+
+			TRACE("Device inode %x:%x, rdev %x\n",
+					SQUASHFS_INODE_BLK(inode), offset,
+					inodep->rdev);
+			break;
+		 }
+		 case SQUASHFS_FIFO_TYPE:
+		 case SQUASHFS_SOCKET_TYPE: {
+
+			i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
+							? S_IFIFO : S_IFSOCK;
+			init_special_inode(i, i->i_mode, 0);
+			break;
+		 }
+		 default:
+			ERROR("Unknown inode type %d in squashfs_iget!\n",
+					inodeb->inode_type);
+			goto failed_read1;
+	}
+	
+	return 1;
+
+failed_read:
+	ERROR("Unable to read inode [%x:%x]\n", block, offset);
+
+failed_read1:
+	return 0;
+}
+
+
+static int get_dir_index_using_offset(struct super_block *s, long long 
+				*next_block, unsigned int *next_offset,
+				long long index_start,
+				unsigned int index_offset, int i_count,
+				long long f_pos)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	int i, length = 0;
+	struct squashfs_dir_index_2 index;
+
+	TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
+					i_count, (unsigned int) f_pos);
+
+	if (f_pos == 0)
+		goto finish;
+
+	for (i = 0; i < i_count; i++) {
+		if (msblk->swap) {
+			struct squashfs_dir_index_2 sindex;
+			squashfs_get_cached_block(s, (char *) &sindex,
+					index_start, index_offset,
+					sizeof(sindex), &index_start,
+					&index_offset);
+			SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
+		} else
+			squashfs_get_cached_block(s, (char *) &index,
+					index_start, index_offset,
+					sizeof(index), &index_start,
+					&index_offset);
+
+		if (index.index > f_pos)
+			break;
+
+		squashfs_get_cached_block(s, NULL, index_start, index_offset,
+					index.size + 1, &index_start,
+					&index_offset);
+
+		length = index.index;
+		*next_block = index.start_block + sblk->directory_table_start;
+	}
+
+	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+
+finish:
+	return length;
+}
+
+
+static int get_dir_index_using_name(struct super_block *s, long long
+				*next_block, unsigned int *next_offset,
+				long long index_start,
+				unsigned int index_offset, int i_count,
+				const char *name, int size)
+{
+	struct squashfs_sb_info *msblk = s->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	int i, length = 0;
+	struct squashfs_dir_index_2 *index;
+	char *str;
+
+	TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
+
+	if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
+		(SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
+		ERROR("Failed to allocate squashfs_dir_index\n");
+		goto failure;
+	}
+
+	index = (struct squashfs_dir_index_2 *) (str + SQUASHFS_NAME_LEN + 1);
+	strncpy(str, name, size);
+	str[size] = '\0';
+
+	for (i = 0; i < i_count; i++) {
+		if (msblk->swap) {
+			struct squashfs_dir_index_2 sindex;
+			squashfs_get_cached_block(s, (char *) &sindex,
+					index_start, index_offset,
+					sizeof(sindex), &index_start,
+					&index_offset);
+			SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
+		} else
+			squashfs_get_cached_block(s, (char *) index,
+					index_start, index_offset,
+					sizeof(struct squashfs_dir_index_2),
+					&index_start, &index_offset);
+
+		squashfs_get_cached_block(s, index->name, index_start,
+					index_offset, index->size + 1,
+					&index_start, &index_offset);
+
+		index->name[index->size + 1] = '\0';
+
+		if (strcmp(index->name, str) > 0)
+			break;
+
+		length = index->index;
+		*next_block = index->start_block + sblk->directory_table_start;
+	}
+
+	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+	kfree(str);
+failure:
+	return length;
+}
+
+		
+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
+{
+	struct inode *i = file->f_dentry->d_inode;
+	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	long long next_block = SQUASHFS_I(i)->start_block +
+		sblk->directory_table_start;
+	int next_offset = SQUASHFS_I(i)->offset, length = 0,
+		dir_count;
+	struct squashfs_dir_header_2 dirh;
+	struct squashfs_dir_entry_2 *dire;
+
+	TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
+
+	if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
+		SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
+		ERROR("Failed to allocate squashfs_dir_entry\n");
+		goto finish;
+	}
+
+	length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
+				SQUASHFS_I(i)->u.s2.directory_index_start,
+				SQUASHFS_I(i)->u.s2.directory_index_offset,
+				SQUASHFS_I(i)->u.s2.directory_index_count,
+				file->f_pos);
+
+	while (length < i_size_read(i)) {
+		/* read directory header */
+		if (msblk->swap) {
+			struct squashfs_dir_header_2 sdirh;
+			
+			if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+					next_block, next_offset, sizeof(sdirh),
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += sizeof(sdirh);
+			SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
+		} else {
+			if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+					next_block, next_offset, sizeof(dirh),
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += sizeof(dirh);
+		}
+
+		dir_count = dirh.count + 1;
+		while (dir_count--) {
+			if (msblk->swap) {
+				struct squashfs_dir_entry_2 sdire;
+				if (!squashfs_get_cached_block(i->i_sb, (char *)
+						&sdire, next_block, next_offset,
+						sizeof(sdire), &next_block,
+						&next_offset))
+					goto failed_read;
+				
+				length += sizeof(sdire);
+				SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
+			} else {
+				if (!squashfs_get_cached_block(i->i_sb, (char *)
+						dire, next_block, next_offset,
+						sizeof(*dire), &next_block,
+						&next_offset))
+					goto failed_read;
+
+				length += sizeof(*dire);
+			}
+
+			if (!squashfs_get_cached_block(i->i_sb, dire->name,
+						next_block, next_offset,
+						dire->size + 1, &next_block,
+						&next_offset))
+				goto failed_read;
+
+			length += dire->size + 1;
+
+			if (file->f_pos >= length)
+				continue;
+
+			dire->name[dire->size + 1] = '\0';
+
+			TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
+					(unsigned int) dirent, dire->name,
+					dire->size + 1, (int) file->f_pos,
+					dirh.start_block, dire->offset,
+					squashfs_filetype_table[dire->type]);
+
+			if (filldir(dirent, dire->name, dire->size + 1,
+					file->f_pos, SQUASHFS_MK_VFS_INODE(
+					dirh.start_block, dire->offset),
+					squashfs_filetype_table[dire->type])
+					< 0) {
+				TRACE("Filldir returned less than 0\n");
+				goto finish;
+			}
+			file->f_pos = length;
+		}
+	}
+
+finish:
+	kfree(dire);
+	return 0;
+
+failed_read:
+	ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+		next_offset);
+	kfree(dire);
+	return 0;
+}
+
+
+static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry,
+				struct nameidata *nd)
+{
+	const unsigned char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct inode *inode = NULL;
+	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+	struct squashfs_super_block *sblk = &msblk->sblk;
+	long long next_block = SQUASHFS_I(i)->start_block +
+				sblk->directory_table_start;
+	int next_offset = SQUASHFS_I(i)->offset, length = 0,
+				dir_count;
+	struct squashfs_dir_header_2 dirh;
+	struct squashfs_dir_entry_2 *dire;
+	int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
+
+	TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset);
+
+	if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
+		SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
+		ERROR("Failed to allocate squashfs_dir_entry\n");
+		goto exit_loop;
+	}
+
+	if (len > SQUASHFS_NAME_LEN)
+		goto exit_loop;
+
+	length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
+				SQUASHFS_I(i)->u.s2.directory_index_start,
+				SQUASHFS_I(i)->u.s2.directory_index_offset,
+				SQUASHFS_I(i)->u.s2.directory_index_count, name,
+				len);
+
+	while (length < i_size_read(i)) {
+		/* read directory header */
+		if (msblk->swap) {
+			struct squashfs_dir_header_2 sdirh;
+			if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+					next_block, next_offset, sizeof(sdirh),
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += sizeof(sdirh);
+			SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
+		} else {
+			if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+					next_block, next_offset, sizeof(dirh),
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += sizeof(dirh);
+		}
+
+		dir_count = dirh.count + 1;
+		while (dir_count--) {
+			if (msblk->swap) {
+				struct squashfs_dir_entry_2 sdire;
+				if (!squashfs_get_cached_block(i->i_sb, (char *)
+						&sdire, next_block,next_offset,
+						sizeof(sdire), &next_block,
+						&next_offset))
+					goto failed_read;
+				
+				length += sizeof(sdire);
+				SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
+			} else {
+				if (!squashfs_get_cached_block(i->i_sb, (char *)
+						dire, next_block,next_offset,
+						sizeof(*dire), &next_block,
+						&next_offset))
+					goto failed_read;
+
+				length += sizeof(*dire);
+			}
+
+			if (!squashfs_get_cached_block(i->i_sb, dire->name,
+					next_block, next_offset, dire->size + 1,
+					&next_block, &next_offset))
+				goto failed_read;
+
+			length += dire->size + 1;
+
+			if (sorted && name[0] < dire->name[0])
+				goto exit_loop;
+
+			if ((len == dire->size + 1) && !strncmp(name,
+						dire->name, len)) {
+				squashfs_inode_t ino =
+					SQUASHFS_MKINODE(dirh.start_block,
+					dire->offset);
+				unsigned int inode_number = SQUASHFS_MK_VFS_INODE(dirh.start_block,
+					dire->offset);
+
+				TRACE("calling squashfs_iget for directory "
+					"entry %s, inode %x:%x, %lld\n", name,
+					dirh.start_block, dire->offset, ino);
+
+				inode = squashfs_iget(i->i_sb, ino, inode_number);
+
+				goto exit_loop;
+			}
+		}
+	}
+
+exit_loop:
+	kfree(dire);
+	d_add(dentry, inode);
+	return ERR_PTR(0);
+
+failed_read:
+	ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+		next_offset);
+	goto exit_loop;
+}
+
+
+int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
+{
+	struct squashfs_super_block *sblk = &msblk->sblk;
+
+	msblk->read_inode = squashfs_read_inode_2;
+	msblk->read_fragment_index_table = read_fragment_index_table_2;
+
+	sblk->bytes_used = sblk->bytes_used_2;
+	sblk->uid_start = sblk->uid_start_2;
+	sblk->guid_start = sblk->guid_start_2;
+	sblk->inode_table_start = sblk->inode_table_start_2;
+	sblk->directory_table_start = sblk->directory_table_start_2;
+	sblk->fragment_table_start = sblk->fragment_table_start_2;
+
+	return 1;
+}
Index: linux-2.6.22/include/linux/squashfs_fs.h
===================================================================
--- /dev/null
+++ linux-2.6.22/include/linux/squashfs_fs.h
@@ -0,0 +1,934 @@
+#ifndef SQUASHFS_FS
+#define SQUASHFS_FS
+
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
+ * Phillip Lougher <phillip@lougher.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs.h
+ */
+
+#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
+#endif
+
+#ifdef	CONFIG_SQUASHFS_VMALLOC
+#define SQUASHFS_ALLOC(a)		vmalloc(a)
+#define SQUASHFS_FREE(a)		vfree(a)
+#else
+#define SQUASHFS_ALLOC(a)		kmalloc(a, GFP_KERNEL)
+#define SQUASHFS_FREE(a)		kfree(a)
+#endif
+#define SQUASHFS_CACHED_FRAGMENTS	CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE	
+#define SQUASHFS_MAJOR			3
+#define SQUASHFS_MINOR			0
+#define SQUASHFS_MAGIC			0x73717368
+#define SQUASHFS_MAGIC_SWAP		0x68737173
+#define SQUASHFS_START			0
+
+/* size of metadata (inode and directory) blocks */
+#define SQUASHFS_METADATA_SIZE		8192
+#define SQUASHFS_METADATA_LOG		13
+
+/* default size of data blocks */
+#define SQUASHFS_FILE_SIZE		65536
+#define SQUASHFS_FILE_LOG		16
+
+#define SQUASHFS_FILE_MAX_SIZE		65536
+
+/* Max number of uids and gids */
+#define SQUASHFS_UIDS			256
+#define SQUASHFS_GUIDS			255
+
+/* Max length of filename (not 255) */
+#define SQUASHFS_NAME_LEN		256
+
+#define SQUASHFS_INVALID		((long long) 0xffffffffffff)
+#define SQUASHFS_INVALID_FRAG		((unsigned int) 0xffffffff)
+#define SQUASHFS_INVALID_BLK		((long long) -1)
+#define SQUASHFS_USED_BLK		((long long) -2)
+
+/* Filesystem flags */
+#define SQUASHFS_NOI			0
+#define SQUASHFS_NOD			1
+#define SQUASHFS_CHECK			2
+#define SQUASHFS_NOF			3
+#define SQUASHFS_NO_FRAG		4
+#define SQUASHFS_ALWAYS_FRAG		5
+#define SQUASHFS_DUPLICATE		6
+#define SQUASHFS_EXPORT			7
+
+#define SQUASHFS_BIT(flag, bit)		((flag >> bit) & 1)
+
+#define SQUASHFS_UNCOMPRESSED_INODES(flags)	SQUASHFS_BIT(flags, \
+						SQUASHFS_NOI)
+
+#define SQUASHFS_UNCOMPRESSED_DATA(flags)	SQUASHFS_BIT(flags, \
+						SQUASHFS_NOD)
+
+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags)	SQUASHFS_BIT(flags, \
+						SQUASHFS_NOF)
+
+#define SQUASHFS_NO_FRAGMENTS(flags)		SQUASHFS_BIT(flags, \
+						SQUASHFS_NO_FRAG)
+
+#define SQUASHFS_ALWAYS_FRAGMENTS(flags)	SQUASHFS_BIT(flags, \
+						SQUASHFS_ALWAYS_FRAG)
+
+#define SQUASHFS_DUPLICATES(flags)		SQUASHFS_BIT(flags, \
+						SQUASHFS_DUPLICATE)
+
+#define SQUASHFS_EXPORTABLE(flags)		SQUASHFS_BIT(flags, \
+						SQUASHFS_EXPORT)
+
+#define SQUASHFS_CHECK_DATA(flags)		SQUASHFS_BIT(flags, \
+						SQUASHFS_CHECK)
+
+#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
+		duplicate_checking, exortable)	(noi | (nod << 1) | (check_data << 2) \
+		| (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
+		(duplicate_checking << 6) | (exportable << 7))
+
+/* Max number of types and file types */
+#define SQUASHFS_DIR_TYPE		1
+#define SQUASHFS_FILE_TYPE		2
+#define SQUASHFS_SYMLINK_TYPE		3
+#define SQUASHFS_BLKDEV_TYPE		4
+#define SQUASHFS_CHRDEV_TYPE		5
+#define SQUASHFS_FIFO_TYPE		6
+#define SQUASHFS_SOCKET_TYPE		7
+#define SQUASHFS_LDIR_TYPE		8
+#define SQUASHFS_LREG_TYPE		9
+
+/* 1.0 filesystem type definitions */
+#define SQUASHFS_TYPES			5
+#define SQUASHFS_IPC_TYPE		0
+
+/* Flag whether block is compressed or uncompressed, bit is set if block is
+ * uncompressed */
+#define SQUASHFS_COMPRESSED_BIT		(1 << 15)
+
+#define SQUASHFS_COMPRESSED_SIZE(B)	(((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
+		(B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
+
+#define SQUASHFS_COMPRESSED(B)		(!((B) & SQUASHFS_COMPRESSED_BIT))
+
+#define SQUASHFS_COMPRESSED_BIT_BLOCK		(1 << 24)
+
+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)	(((B) & \
+	~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
+	~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
+
+#define SQUASHFS_COMPRESSED_BLOCK(B)	(!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
+
+/*
+ * Inode number ops.  Inodes consist of a compressed block number, and an
+ * uncompressed  offset within that block
+ */
+#define SQUASHFS_INODE_BLK(a)		((unsigned int) ((a) >> 16))
+
+#define SQUASHFS_INODE_OFFSET(a)	((unsigned int) ((a) & 0xffff))
+
+#define SQUASHFS_MKINODE(A, B)		((squashfs_inode_t)(((squashfs_inode_t) (A)\
+					<< 16) + (B)))
+
+/* Compute 32 bit VFS inode number from squashfs inode number */
+#define SQUASHFS_MK_VFS_INODE(a, b)	((unsigned int) (((a) << 8) + \
+					((b) >> 2) + 1))
+/* XXX */
+
+/* Translate between VFS mode and squashfs mode */
+#define SQUASHFS_MODE(a)		((a) & 0xfff)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES(A)	((A) * sizeof(struct squashfs_fragment_entry))
+
+#define SQUASHFS_FRAGMENT_INDEX(A)	(SQUASHFS_FRAGMENT_BYTES(A) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)	(SQUASHFS_FRAGMENT_BYTES(A) % \
+						SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES(A)	((SQUASHFS_FRAGMENT_BYTES(A) + \
+					SQUASHFS_METADATA_SIZE - 1) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)	(SQUASHFS_FRAGMENT_INDEXES(A) *\
+						sizeof(long long))
+
+/* inode lookup table defines */
+#define SQUASHFS_LOOKUP_BYTES(A)	((A) * sizeof(squashfs_inode_t))
+
+#define SQUASHFS_LOOKUP_BLOCK(A)		(SQUASHFS_LOOKUP_BYTES(A) / \
+						SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A)		(SQUASHFS_LOOKUP_BYTES(A) % \
+						SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCKS(A)	((SQUASHFS_LOOKUP_BYTES(A) + \
+					SQUASHFS_METADATA_SIZE - 1) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_BYTES(A)	(SQUASHFS_LOOKUP_BLOCKS(A) *\
+					sizeof(long long))
+
+/* cached data constants for filesystem */
+#define SQUASHFS_CACHED_BLKS		8
+
+#define SQUASHFS_MAX_FILE_SIZE_LOG	64
+
+#define SQUASHFS_MAX_FILE_SIZE		((long long) 1 << \
+					(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
+
+#define SQUASHFS_MARKER_BYTE		0xff
+
+/* meta index cache */
+#define SQUASHFS_META_INDEXES	(SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
+#define SQUASHFS_META_ENTRIES	31
+#define SQUASHFS_META_NUMBER	8
+#define SQUASHFS_SLOTS		4
+
+struct meta_entry {
+	long long		data_block;
+	unsigned int		index_block;
+	unsigned short		offset;
+	unsigned short		pad;
+};
+
+struct meta_index {
+	unsigned int		inode_number;
+	unsigned int		offset;
+	unsigned short		entries;
+	unsigned short		skip;
+	unsigned short		locked;
+	unsigned short		pad;
+	struct meta_entry	meta_entry[SQUASHFS_META_ENTRIES];
+};
+
+
+/*
+ * definitions for structures on disk
+ */
+
+typedef long long		squashfs_block_t;
+typedef long long		squashfs_inode_t;
+
+struct squashfs_super_block {
+	unsigned int		s_magic;
+	unsigned int		inodes;
+	unsigned int		bytes_used_2;
+	unsigned int		uid_start_2;
+	unsigned int		guid_start_2;
+	unsigned int		inode_table_start_2;
+	unsigned int		directory_table_start_2;
+	unsigned int		s_major:16;
+	unsigned int		s_minor:16;
+	unsigned int		block_size_1:16;
+	unsigned int		block_log:16;
+	unsigned int		flags:8;
+	unsigned int		no_uids:8;
+	unsigned int		no_guids:8;
+	unsigned int		mkfs_time /* time of filesystem creation */;
+	squashfs_inode_t	root_inode;
+	unsigned int		block_size;
+	unsigned int		fragments;
+	unsigned int		fragment_table_start_2;
+	long long		bytes_used;
+	long long		uid_start;
+	long long		guid_start;
+	long long		inode_table_start;
+	long long		directory_table_start;
+	long long		fragment_table_start;
+	long long		lookup_table_start;
+} __attribute__ ((packed));
+
+struct squashfs_dir_index {
+	unsigned int		index;
+	unsigned int		start_block;
+	unsigned char		size;
+	unsigned char		name[0];
+} __attribute__ ((packed));
+
+#define SQUASHFS_BASE_INODE_HEADER		\
+	unsigned int		inode_type:4;	\
+	unsigned int		mode:12;	\
+	unsigned int		uid:8;		\
+	unsigned int		guid:8;		\
+	unsigned int		mtime;		\
+	unsigned int 		inode_number;
+
+struct squashfs_base_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	unsigned short		rdev;
+} __attribute__ ((packed));
+	
+struct squashfs_symlink_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	unsigned short		symlink_size;
+	char			symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	squashfs_block_t	start_block;
+	unsigned int		fragment;
+	unsigned int		offset;
+	unsigned int		file_size;
+	unsigned short		block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_lreg_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	squashfs_block_t	start_block;
+	unsigned int		fragment;
+	unsigned int		offset;
+	long long		file_size;
+	unsigned short		block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	unsigned int		file_size:19;
+	unsigned int		offset:13;
+	unsigned int		start_block;
+	unsigned int		parent_inode;
+} __attribute__  ((packed));
+
+struct squashfs_ldir_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	unsigned int		file_size:27;
+	unsigned int		offset:13;
+	unsigned int		start_block;
+	unsigned int		i_count:16;
+	unsigned int		parent_inode;
+	struct squashfs_dir_index	index[0];
+} __attribute__  ((packed));
+
+union squashfs_inode_header {
+	struct squashfs_base_inode_header	base;
+	struct squashfs_dev_inode_header	dev;
+	struct squashfs_symlink_inode_header	symlink;
+	struct squashfs_reg_inode_header	reg;
+	struct squashfs_lreg_inode_header	lreg;
+	struct squashfs_dir_inode_header	dir;
+	struct squashfs_ldir_inode_header	ldir;
+	struct squashfs_ipc_inode_header	ipc;
+};
+	
+struct squashfs_dir_entry {
+	unsigned int		offset:13;
+	unsigned int		type:3;
+	unsigned int		size:8;
+	int			inode_number:16;
+	char			name[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_header {
+	unsigned int		count:8;
+	unsigned int		start_block;
+	unsigned int		inode_number;
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry {
+	long long		start_block;
+	unsigned int		size;
+	unsigned int		pending;
+} __attribute__ ((packed));
+
+extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
+extern int squashfs_uncompress_init(void);
+extern int squashfs_uncompress_exit(void);
+
+/*
+ * macros to convert each packed bitfield structure from little endian to big
+ * endian and vice versa.  These are needed when creating or using a filesystem
+ * on a machine with different byte ordering to the target architecture.
+ *
+ */
+
+#define SQUASHFS_SWAP_START \
+	int bits;\
+	int b_pos;\
+	unsigned long long val;\
+	unsigned char *s;\
+	unsigned char *d;
+
+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
+	SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
+	SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
+	SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
+	SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
+	SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
+	SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
+	SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
+	SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
+	SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
+	SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
+	SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
+	SQUASHFS_SWAP((s)->flags, d, 288, 8);\
+	SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
+	SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
+	SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
+	SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
+	SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
+	SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
+	SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
+	SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
+	SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
+	SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
+	SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
+	SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
+	SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
+	SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
+}
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
+	SQUASHFS_MEMSET(s, d, n);\
+	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+	SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+	SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+	SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+	SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+	SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_ipc_inode_header))\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_dev_inode_header)); \
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_symlink_inode_header));\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_reg_inode_header));\
+	SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
+	SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
+	SQUASHFS_SWAP((s)->offset, d, 192, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
+}
+
+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_lreg_inode_header));\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
+	SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
+	SQUASHFS_SWAP((s)->offset, d, 224, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_dir_inode_header));\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
+	SQUASHFS_SWAP((s)->offset, d, 147, 13);\
+	SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
+	SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_ldir_inode_header));\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
+	SQUASHFS_SWAP((s)->offset, d, 155, 13);\
+	SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
+	SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
+	SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
+	SQUASHFS_SWAP((s)->index, d, 0, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
+	SQUASHFS_SWAP((s)->size, d, 64, 8);\
+}
+
+#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
+	SQUASHFS_SWAP((s)->count, d, 0, 8);\
+	SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
+	SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
+	SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+	SQUASHFS_SWAP((s)->type, d, 13, 3);\
+	SQUASHFS_SWAP((s)->size, d, 16, 8);\
+	SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
+	SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
+	SQUASHFS_SWAP((s)->size, d, 64, 32);\
+}
+
+#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
+
+#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
+	int entry;\
+	int bit_position;\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, n * 2);\
+	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+			16)\
+		SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
+}
+
+#define SQUASHFS_SWAP_INTS(s, d, n) {\
+	int entry;\
+	int bit_position;\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, n * 4);\
+	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+			32)\
+		SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
+}
+
+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
+	int entry;\
+	int bit_position;\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, n * 8);\
+	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+			64)\
+		SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
+}
+
+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
+	int entry;\
+	int bit_position;\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, n * bits / 8);\
+	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+			bits)\
+		SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+
+struct squashfs_base_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned int		type:4;
+	unsigned int		offset:4;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned short		rdev;
+} __attribute__ ((packed));
+	
+struct squashfs_symlink_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned short		symlink_size;
+	char			symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned int		mtime;
+	unsigned int		start_block;
+	unsigned int		file_size:32;
+	unsigned short		block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned int		file_size:19;
+	unsigned int		offset:13;
+	unsigned int		mtime;
+	unsigned int		start_block:24;
+} __attribute__  ((packed));
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
+	SQUASHFS_MEMSET(s, d, n);\
+	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+	SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+	SQUASHFS_SWAP((s)->uid, d, 16, 4);\
+	SQUASHFS_SWAP((s)->guid, d, 20, 4);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_ipc_inode_header_1));\
+	SQUASHFS_SWAP((s)->type, d, 24, 4);\
+	SQUASHFS_SWAP((s)->offset, d, 28, 4);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_dev_inode_header_1));\
+	SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_symlink_inode_header_1));\
+	SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_reg_inode_header_1));\
+	SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_dir_inode_header_1));\
+	SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
+	SQUASHFS_SWAP((s)->offset, d, 43, 13);\
+	SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
+}
+
+#endif
+
+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+
+struct squashfs_dir_index_2 {
+	unsigned int		index:27;
+	unsigned int		start_block:29;
+	unsigned char		size;
+	unsigned char		name[0];
+} __attribute__ ((packed));
+
+struct squashfs_base_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned short		rdev;
+} __attribute__ ((packed));
+	
+struct squashfs_symlink_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned short		symlink_size;
+	char			symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned int		mtime;
+	unsigned int		start_block;
+	unsigned int		fragment;
+	unsigned int		offset;
+	unsigned int		file_size:32;
+	unsigned short		block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned int		file_size:19;
+	unsigned int		offset:13;
+	unsigned int		mtime;
+	unsigned int		start_block:24;
+} __attribute__  ((packed));
+
+struct squashfs_ldir_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned int		file_size:27;
+	unsigned int		offset:13;
+	unsigned int		mtime;
+	unsigned int		start_block:24;
+	unsigned int		i_count:16;
+	struct squashfs_dir_index_2	index[0];
+} __attribute__  ((packed));
+
+union squashfs_inode_header_2 {
+	struct squashfs_base_inode_header_2	base;
+	struct squashfs_dev_inode_header_2	dev;
+	struct squashfs_symlink_inode_header_2	symlink;
+	struct squashfs_reg_inode_header_2	reg;
+	struct squashfs_dir_inode_header_2	dir;
+	struct squashfs_ldir_inode_header_2	ldir;
+	struct squashfs_ipc_inode_header_2	ipc;
+};
+	
+struct squashfs_dir_header_2 {
+	unsigned int		count:8;
+	unsigned int		start_block:24;
+} __attribute__ ((packed));
+
+struct squashfs_dir_entry_2 {
+	unsigned int		offset:13;
+	unsigned int		type:3;
+	unsigned int		size:8;
+	char			name[0];
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry_2 {
+	unsigned int		start_block;
+	unsigned int		size;
+} __attribute__ ((packed));
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+	SQUASHFS_MEMSET(s, d, n);\
+	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+	SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+	SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+	SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
+	SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_dev_inode_header_2)); \
+	SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_symlink_inode_header_2));\
+	SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_reg_inode_header_2));\
+	SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
+	SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
+	SQUASHFS_SWAP((s)->offset, d, 128, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_dir_inode_header_2));\
+	SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
+	SQUASHFS_SWAP((s)->offset, d, 51, 13);\
+	SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_ldir_inode_header_2));\
+	SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
+	SQUASHFS_SWAP((s)->offset, d, 59, 13);\
+	SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
+	SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
+	SQUASHFS_SWAP((s)->index, d, 0, 27);\
+	SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
+	SQUASHFS_SWAP((s)->size, d, 56, 8);\
+}
+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
+	SQUASHFS_SWAP((s)->count, d, 0, 8);\
+	SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
+	SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+	SQUASHFS_SWAP((s)->type, d, 13, 3);\
+	SQUASHFS_SWAP((s)->size, d, 16, 8);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
+	SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
+	SQUASHFS_SWAP((s)->size, d, 32, 32);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES_2(A)	(A * sizeof(struct squashfs_fragment_entry_2))
+
+#define SQUASHFS_FRAGMENT_INDEX_2(A)	(SQUASHFS_FRAGMENT_BYTES_2(A) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A)	(SQUASHFS_FRAGMENT_BYTES_2(A) % \
+						SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES_2(A)	((SQUASHFS_FRAGMENT_BYTES_2(A) + \
+					SQUASHFS_METADATA_SIZE - 1) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A)	(SQUASHFS_FRAGMENT_INDEXES_2(A) *\
+						sizeof(int))
+
+#endif
+
+#ifdef __KERNEL__
+
+/*
+ * macros used to swap each structure entry, taking into account
+ * bitfields and different bitfield placing conventions on differing
+ * architectures
+ */
+
+#include <asm/byteorder.h>
+
+#ifdef __BIG_ENDIAN
+	/* convert from little endian to big endian */
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
+		tbits, b_pos)
+#else
+	/* convert from big endian to little endian */ 
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
+		tbits, 64 - tbits - b_pos)
+#endif
+
+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
+	b_pos = pos % 8;\
+	val = 0;\
+	s = (unsigned char *)p + (pos / 8);\
+	d = ((unsigned char *) &val) + 7;\
+	for(bits = 0; bits < (tbits + b_pos); bits += 8) \
+		*d-- = *s++;\
+	value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
+}
+
+#define SQUASHFS_MEMSET(s, d, n)	memset(s, 0, n);
+
+#endif
+#endif
Index: linux-2.6.22/include/linux/squashfs_fs_i.h
===================================================================
--- /dev/null
+++ linux-2.6.22/include/linux/squashfs_fs_i.h
@@ -0,0 +1,45 @@
+#ifndef SQUASHFS_FS_I
+#define SQUASHFS_FS_I
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
+ * Phillip Lougher <phillip@lougher.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs_i.h
+ */
+
+struct squashfs_inode_info {
+	long long	start_block;
+	unsigned int	offset;
+	union {
+		struct {
+			long long	fragment_start_block;
+			unsigned int	fragment_size;
+			unsigned int	fragment_offset;
+			long long	block_list_start;
+		} s1;
+		struct {
+			long long	directory_index_start;
+			unsigned int	directory_index_offset;
+			unsigned int	directory_index_count;
+			unsigned int	parent_inode;
+		} s2;
+	} u;
+	struct inode	vfs_inode;
+};
+#endif
Index: linux-2.6.22/include/linux/squashfs_fs_sb.h
===================================================================
--- /dev/null
+++ linux-2.6.22/include/linux/squashfs_fs_sb.h
@@ -0,0 +1,74 @@
+#ifndef SQUASHFS_FS_SB
+#define SQUASHFS_FS_SB
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
+ * Phillip Lougher <phillip@lougher.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs_sb.h
+ */
+
+#include <linux/squashfs_fs.h>
+
+struct squashfs_cache {
+	long long	block;
+	int		length;
+	long long	next_index;
+	char		*data;
+};
+
+struct squashfs_fragment_cache {
+	long long	block;
+	int		length;
+	unsigned int	locked;
+	char		*data;
+};
+
+struct squashfs_sb_info {
+	struct squashfs_super_block	sblk;
+	int			devblksize;
+	int			devblksize_log2;
+	int			swap;
+	struct squashfs_cache	*block_cache;
+	struct squashfs_fragment_cache	*fragment;
+	int			next_cache;
+	int			next_fragment;
+	int			next_meta_index;
+	unsigned int		*uid;
+	unsigned int		*guid;
+	long long		*fragment_index;
+	unsigned int		*fragment_index_2;
+	char			*read_page;
+	struct mutex		read_data_mutex;
+	struct mutex		read_page_mutex;
+	struct mutex		block_cache_mutex;
+	struct mutex		fragment_mutex;
+	struct mutex		meta_index_mutex;
+	wait_queue_head_t	waitq;
+	wait_queue_head_t	fragment_wait_queue;
+	struct meta_index	*meta_index;
+	z_stream		stream;
+	long long		*inode_lookup_table;
+	int			(*read_inode)(struct inode *i,  squashfs_inode_t \
+				inode);
+	long long		(*read_blocklist)(struct inode *inode, int \
+				index, int readahead_blks, char *block_list, \
+				unsigned short **block_p, unsigned int *bsize);
+	int			(*read_fragment_index_table)(struct super_block *s);
+};
+#endif
Index: linux-2.6.22/init/do_mounts_rd.c
===================================================================
--- linux-2.6.22.orig/init/do_mounts_rd.c
+++ linux-2.6.22/init/do_mounts_rd.c
@@ -5,6 +5,7 @@
 #include <linux/ext2_fs.h>
 #include <linux/romfs_fs.h>
 #include <linux/cramfs_fs.h>
+#include <linux/squashfs_fs.h>
 #include <linux/initrd.h>
 #include <linux/string.h>
 
@@ -39,6 +40,7 @@ static int __init crd_load(int in_fd, in
  * numbers could not be found.
  *
  * We currently check for the following magic numbers:
+ *      squashfs
  * 	minix
  * 	ext2
  *	romfs
@@ -53,6 +55,7 @@ identify_ramdisk_image(int fd, int start
 	struct ext2_super_block *ext2sb;
 	struct romfs_super_block *romfsb;
 	struct cramfs_super *cramfsb;
+	struct squashfs_super_block *squashfsb;
 	int nblocks = -1;
 	unsigned char *buf;
 
@@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start
 	ext2sb = (struct ext2_super_block *) buf;
 	romfsb = (struct romfs_super_block *) buf;
 	cramfsb = (struct cramfs_super *) buf;
+	squashfsb = (struct squashfs_super_block *) buf;
 	memset(buf, 0xe5, size);
 
 	/*
@@ -101,6 +105,18 @@ identify_ramdisk_image(int fd, int start
 		goto done;
 	}
 
+	/* squashfs is at block zero too */
+	if (squashfsb->s_magic == SQUASHFS_MAGIC) {
+		printk(KERN_NOTICE
+		       "RAMDISK: squashfs filesystem found at block %d\n",
+		       start_block);
+		if (squashfsb->s_major < 3)
+			nblocks = (squashfsb->bytes_used_2+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
+		else
+			nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
+		goto done;
+	}
+
 	/*
 	 * Read block 1 to test for minix and ext2 superblock
 	 */
Index: linux-2.6.22/arch/alpha/Kconfig
===================================================================
--- linux-2.6.22.orig/arch/alpha/Kconfig
+++ linux-2.6.22/arch/alpha/Kconfig
@@ -613,6 +613,32 @@ config VERBOSE_MCHECK_ON
 
 	  Take the default (1) unless you want more control or more info.
 
+config ALPHA_UAC_SYSCTL
+	bool "Configure UAC policy via sysctl"
+	depends on SYSCTL
+	default y
+	---help---
+	  Configuring the UAC (unaligned access control) policy on a Linux
+	  system usually involves setting a compile time define. If you say
+	  Y here, you will be able to modify the UAC policy at runtime using
+	  the /proc interface.
+
+	  The UAC policy defines the action Linux should take when an
+	  unaligned memory access occurs. The action can include printing a
+	  warning message (NOPRINT), sending a signal to the offending
+	  program to help developers debug their applications (SIGBUS), or
+	  disabling the transparent fixing (NOFIX).
+
+	  The sysctls will be initialized to the compile-time defined UAC
+	  policy. You can change these manually, or with the sysctl(8)
+	  userspace utility.
+
+	  To disable the warning messages at runtime, you would use
+
+	    echo 1 > /proc/sys/kernel/uac/noprint
+
+	  This is pretty harmless. Say Y if you're not sure.
+
 source "drivers/pci/Kconfig"
 source "drivers/eisa/Kconfig"
 
Index: linux-2.6.22/arch/alpha/kernel/traps.c
===================================================================
--- linux-2.6.22.orig/arch/alpha/kernel/traps.c
+++ linux-2.6.22/arch/alpha/kernel/traps.c
@@ -14,6 +14,7 @@
 #include <linux/delay.h>
 #include <linux/smp_lock.h>
 #include <linux/module.h>
+#include <linux/sysctl.h>
 #include <linux/init.h>
 #include <linux/kallsyms.h>
 
@@ -102,6 +103,38 @@ static char * ireg_name[] = {"v0", "t0",
 			   "t10", "t11", "ra", "pv", "at", "gp", "sp", "zero"};
 #endif
 
+#ifdef CONFIG_ALPHA_UAC_SYSCTL
+static struct ctl_table_header *uac_sysctl_header;
+
+static int enabled_noprint = 0;
+static int enabled_sigbus = 0;
+static int enabled_nofix = 0;
+
+ctl_table uac_table[] = {
+	{KERN_UAC_NOPRINT, "noprint", &enabled_noprint, sizeof (int), 0644, NULL, NULL, &proc_dointvec},
+	{KERN_UAC_SIGBUS, "sigbus", &enabled_sigbus, sizeof (int), 0644, NULL, NULL, &proc_dointvec},
+	{KERN_UAC_NOFIX, "nofix", &enabled_nofix, sizeof (int), 0644, NULL, NULL, &proc_dointvec},
+        {0}
+};
+
+static int __init init_uac_sysctl(void)
+{
+	/* Initialize sysctls with the #defined UAC policy */
+	enabled_noprint = (test_thread_flag (TIF_UAC_NOPRINT)) ? 1 : 0;
+	enabled_sigbus = (test_thread_flag (TIF_UAC_SIGBUS)) ? 1 : 0;
+	enabled_nofix = (test_thread_flag (TIF_UAC_NOFIX)) ? 1 : 0;
+
+	/* save this for later so we can clean up */
+	uac_sysctl_header = register_sysctl_table(uac_table);
+	return 0;
+}
+
+static void __exit exit_uac_sysctl(void)
+{
+	unregister_sysctl_table(uac_sysctl_header);
+}
+#endif
+
 static void
 dik_show_code(unsigned int *pc)
 {
@@ -779,7 +812,11 @@ do_entUnaUser(void __user * va, unsigned
 	/* Check the UAC bits to decide what the user wants us to do
 	   with the unaliged access.  */
 
+#ifndef CONFIG_ALPHA_UAC_SYSCTL
 	if (!test_thread_flag (TIF_UAC_NOPRINT)) {
+#else  /* CONFIG_ALPHA_UAC_SYSCTL */
+	if (!(enabled_noprint)) {
+#endif /* CONFIG_ALPHA_UAC_SYSCTL */
 		if (cnt >= 5 && jiffies - last_time > 5*HZ) {
 			cnt = 0;
 		}
@@ -790,10 +827,18 @@ do_entUnaUser(void __user * va, unsigned
 		}
 		last_time = jiffies;
 	}
+#ifndef CONFIG_ALPHA_UAC_SYSCTL
 	if (test_thread_flag (TIF_UAC_SIGBUS))
+#else  /* CONFIG_ALPHA_UAC_SYSCTL */
+	if (enabled_sigbus)
+#endif /* CONFIG_ALPHA_UAC_SYSCTL */
 		goto give_sigbus;
 	/* Not sure why you'd want to use this, but... */
+#ifndef CONFIG_ALPHA_UAC_SYSCTL
 	if (test_thread_flag (TIF_UAC_NOFIX))
+#else  /* CONFIG_ALPHA_UAC_SYSCTL */
+	if (enabled_nofix)
+#endif /* CONFIG_ALPHA_UAC_SYSCTL */
 		return;
 
 	/* Don't bother reading ds in the access check since we already
@@ -1088,3 +1133,7 @@ trap_init(void)
 	wrent(entSys, 5);
 	wrent(entDbg, 6);
 }
+
+#ifdef CONFIG_ALPHA_UAC_SYSCTL
+__initcall(init_uac_sysctl);
+#endif

