linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-05-13 19:00 Abhay Salunke
  2005-05-13 21:11 ` Alexey Dobriyan
  0 siblings, 1 reply; 28+ messages in thread
From: Abhay Salunke @ 2005-05-13 19:00 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton; +Cc: abhay_salunke, matt_domsch

This is a resubmit of the patch after incorporating first round of 
suggestson from Andrew Morton.

By making a contribution to this project, I certify that:
The contribution was created in whole or in part by me and I have the 
right to submit it under the open source license indicated in the file.

Signed-off-by: Abhay Salunke <Abhay_Salunke@dell.com>

Thanks,
Abhay Salunke
Software Engineer.
DELL Inc

diff -uprN linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt linux-2.6.11.8/Documentation/DELL_RBU.txt
--- linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.8/Documentation/DELL_RBU.txt	2005-05-11 13:22:45.712684696 -0500
@@ -0,0 +1,92 @@
+Purpose:
+Demonstrate the usage of the DELL_RBU (DELL Remote BIOS Update) driver
+for updating BIOS images on Dell hardware.
+
+Scope:
+This document discusses the functionality of the DELL_RBU driver. 
+This driver is required by BIOS update applications shipped by DELL for updating
+BIOS on DELL servers and client systems. 
+
+Overview:
+The rbu driver is designed to be running on 2.6 kernel. 
+This driver is one single dell_rbu.c file (approx 800 lines total).
+The BIOS update is done by writing the new BIOS image in to contiguous physical
+memory addressable by the BIOS. The user application indicates the BIOS regarding 
+the update of a fresh image. The BIOS then scans the memory to find the image and 
+it will then update itself. There are basically two different mechanisms for 
+writing the BIOS image in to contiguous memory 
+1> By writing the image to one single shunk of contiguous physical memory.
+2> By writing image in to smaller chunks of contiguous physical memory.
+The update mechanism is determined by the update application based on the 
+particular system type.
+
+Update mechanism using single physical chunk of memory:
+The rbu driver on its load time created the following entries in sysfs
+/sys/firmware/rbu/rbudatasize
+/sys/firmware/rbu/rbudata
+
+Steps to update the BIOS image:
+
+1> Set the incoming BIOS image size in the /sys/firmware/rbudatasize file.
+
+ e.g. echo XXXXXX > /sys/firmware/rbudatasize 
+NOTE: the size specified is always in decimal.
+
+you can also read back the image size by doing
+cat /sys/firmware/rbudatasize
+
+2> Download the BIOS image by copying the image file to /sys/firmware/rbudata 
+file.
+e.g. cat image.hdr > /sys/firmware/rbudata
+
+you can also read back the image using 
+cat /sys/firmware/rbu/rbudata
+This is usually helpful in verifying the image downloaded.
+
+Step#1 results in the driver allocating contiguous physical memory  of the size
+echoed in to rbudatasize. The subsequent writes to rbudata as described in
+step #2 results in the image getting written to the allocated contiguous physical 
+pages. Repeating step #2 will overwrite the previous data in rbudata file.
+
+On a driver unload the allocated memory is freed and the rbudatasize file reads 0.
+The user should not unload the driver after downloading the new BIOS image for 
+if it wants to update BIOS with that image.
+
+The user can overwrite the rbudata file with a new image. The user has to make 
+sure that the new image size is less than or equal to the image size copied to 
+the rbudatasize file. 
+If the new image is grater than the allocated size then only the allocated size
+gets copied the rest will not.
+
+The user can also free the previous BIOS image as follows
+echo 0 > /sys/firmware/rbu/rbudatasize
+
+If the user tries to set the BIOS image size there is a possiblity that the 
+system may not have enough contiguous physical memory for upadtes, thus the 
+image allocation will fail. The user the needs to verify this by reading back 
+the rbudatasize which will be set to 0.
+
+Update using smaller chunks (packets) of contiguous memory:
+The disadvantage of contiguous allocation is that it may not be always possible
+to get that size of contiuguous chunk of avaliable physical pages as in most 
+Linux systems the memory gets fragmented immideately after a reboot.
+The update using smaller chunks fixes this issue; it also requires the BIOS on 
+the system to support this feature; the update application needs to query this 
+with the BIOS on the system before using this technique. 
+
+The appplication breaks the BIOS image in to small packets; before starting the 
+update using this technique, the application sets the packetdatasize as follows
+
+echo XXXXXX > /sys/firmware/packetdatasize
+Any writes to /sys/firmware/packetdata results in allocation of contiguous 
+physical memory of packetdatasize and the data is written to that meomry.
+Writing 0 to packetdatasize results in freeing of all packets.
+Unloading the driver will also result in freeing up of the allocated packets.
+
+NOTE:
+Afte updating the BIOS image the appplication needs to communicate with the BIOS 
+for enabling the update on the next reboot. The application can then choose to 
+reboot the system imideately or not reboot the system and leave up to the user 
+to do a reboot.
+
+
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c linux-2.6.11.8/drivers/firmware/dell_rbu.c
--- linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.8/drivers/firmware/dell_rbu.c	2005-05-13 12:38:20.047334448 -0500
@@ -0,0 +1,852 @@
+/*
+ * dell_rbu.c
+ * Bios Update driver for Dell systems
+ * Author: Dell Inc
+ *	   Abhay Salunke <abhay_salunke@dell.com>
+ *
+ * Copyright (C) 2004 Dell Inc.
+ *
+ * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by creating 
+ * entries in the /sys file systems on Linux 2.6 and higher kernels.
+ * The driver supports two mechanism to update the BIOS namely contiguous and packetized.
+ * Both these methods still require to have some application to set the 
+ * CMOS bit indicating the BIOS to update itself after a reboot.
+ * 
+ * Contiguous method:
+ * This driver tries to allocates contiguos physical pages large enough 
+ * to accomodate the BIOS image size specified by the user. The user 
+ * supplied BIOS image is then copied in to the allocated contiguous pages.
+ *
+ * Packetized method:
+ * In case of packetized the driver provides entries in the /sys file systems 
+ * as packetdatasize and packetdata. This driver requires an application to 
+ * break the BIOS image in to fixed sized packet chunks and each packet is written 
+ * to the packetdata entry. The packetdatasize needs to be set once and is fixed 
+ * for all the packets.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation
+ *
+ * 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.
+ *
+ * Changelog:
+ * 
+ * 13 May 2005 Abhay Salunke <Abhay_Salunke@dell.com>
+ * Modified code with suggestions from Andrew Morton; 
+ *
+ */
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/firmware.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#define BIOS_SCAN_LIMIT 0xffffffff
+MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
+MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.6");
+
+static struct _rbu_data {
+	void *image_update_buffer;
+	unsigned long image_update_buffer_size;
+	unsigned long bios_image_size;
+	unsigned long image_update_order_number;
+	spinlock_t lock;
+	unsigned long packet_read_count;
+	unsigned long packet_write_count;
+	unsigned long num_packets;
+	unsigned long packetsize;
+} rbu_data;
+
+struct packet_data{
+	struct list_head list;
+	size_t length;
+	void *data;
+	int ordernum;
+};
+
+
+static struct packet_data packet_data_head;
+
+/* no default attributes yet. */
+static struct attribute * def_attrs[] = { NULL, };
+
+/* don't use show and store attribute functions */
+static struct sysfs_ops rbu_attr_ops = { };
+
+static struct kobj_type ktype_rbu = { 
+	.sysfs_ops	= &rbu_attr_ops,
+	.default_attrs	= def_attrs,
+};
+
+static decl_subsys(rbu,&ktype_rbu,NULL);
+
+void init_packet_head(void)
+{
+	INIT_LIST_HEAD(&packet_data_head.list);
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+static int fill_last_packet(void *data, size_t length)
+{
+	struct list_head *ptemp_list;
+	struct packet_data *ppacket = NULL;
+	int packet_count = 0;
+	
+	pr_debug("fill_last_packet: entry \n");
+
+	/* check if we have any packets */
+	if (0 == rbu_data.num_packets) {
+		pr_debug("fill_last_packet: num_packets=0\n");
+		return -ENOMEM;
+	}
+
+	packet_count = rbu_data.num_packets;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	
+	while(--packet_count) {
+		ptemp_list = ptemp_list->next;
+	}
+
+	ppacket = list_entry(ptemp_list,struct packet_data, list);
+
+	if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
+		printk(KERN_WARNING "fill_last_packet: packet size data overrun\n");
+		return -ENOMEM;
+	}
+	
+	pr_debug("fill_last_packet : buffer = %p\n", ppacket->data);
+
+	/* copy the incoming data in to the new buffer */
+	memcpy(((char *)ppacket->data + rbu_data.packet_write_count), 
+		data, length);
+	
+	if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
+		/* this was the last data chunk in the packet 
+		   so reinitialize the packet data counter to zero */
+		rbu_data.packet_write_count = 0;
+	} else {
+		/* adjust the total packet length */
+		rbu_data.packet_write_count += length;
+	}
+	pr_debug("fill_last_packet: exit \n");
+	return 0;
+}
+
+/*
+ get_free_pages_limited:
+ This is a helper function which allocates free pages based on an upper limit.
+ On x86_64 or 64 bit arch the memory allocation goes above 4GB space which is 
+ not addressable by the BIOS. This function tries to get allocation below the 
+ limit (4GB) address. It first tries to allocate memory normally using the 
+ GFP_KERNEL argument if the incoming limit is non-zero and if the returned 
+ physical memory address exceeds the upper limit, the allocated pages are freed 
+ and the memory is reallocated using the GFP_DMA argument.
+*/
+static void *get_free_pages_limited(unsigned long size,
+                                    int *ordernum,
+				    unsigned long limit)
+{
+	unsigned long img_buf_phys_addr;
+	void *pbuf = NULL;
+
+	*ordernum = get_order(size);
+	/* 
+         * Check if we are not getting a very large file. This can happen as a 
+         * user error in entering the file size 
+        */
+	if (*ordernum == BITS_PER_LONG) {
+		/* The incoming size is very large */
+		pr_debug("get_free_pages_limited: Incoming size is very large\n");
+		return NULL;
+	}
+	
+	/* try allocating a new buffer to fit the request */
+	pbuf =(unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);
+
+	if (pbuf != NULL) {
+        	/* check if the image is with in limits */
+		img_buf_phys_addr = (unsigned long)virt_to_phys((void *)pbuf);
+		
+		if ((limit != 0) && ((img_buf_phys_addr + size) > limit)) {
+			pr_debug("Got memory above 4GB range, free this and try with DMA memory\n");
+			/* free this memory as we need it with in 4GB range */
+			free_pages ((unsigned long)pbuf, *ordernum);
+			/* 
+                         * Try allocating a new buffer from the GFP_DMA range 
+			 * as it is with in 16MB range.
+			 */
+			pbuf =(unsigned char *)__get_free_pages(GFP_DMA, *ordernum);
+			if (pbuf == NULL)
+				pr_debug("Failed to get memory of size %ld using GFP_DMA\n", size);
+		}
+	}
+	return pbuf;
+}
+
+static int create_packet(size_t length)
+{
+	struct packet_data *newpacket;
+	int ordernum = 0;
+
+	pr_debug("create_packet: entry \n");
+
+	if (rbu_data.packetsize == 0 ) {
+		pr_debug("create_packet: packetsize not specified\n");
+		return -EINVAL;
+	}
+
+	newpacket = kmalloc(sizeof(struct packet_data) ,GFP_KERNEL);
+	if(newpacket == NULL) {
+		printk(KERN_WARNING"create_packet: failed to allocate new packet\n");
+		return -ENOMEM;
+	}
+
+	/* there is no upper limit on memory address for packetized mechanism */
+	newpacket->data = get_free_pages_limited(rbu_data.packetsize,&ordernum,	0);
+	pr_debug("create_packet: newpacket %p\n", newpacket->data);
+		
+	if(newpacket->data == NULL) {
+		printk(KERN_WARNING"create_packet: failed to allocate new packet\n");
+		return -ENOMEM;
+	}
+
+	newpacket->ordernum = ordernum;
+	++rbu_data.num_packets;
+	/* initialize the newly created packet headers */
+	INIT_LIST_HEAD(&newpacket->list);
+	/* add this packet to the link list */
+	list_add_tail(&newpacket->list, &packet_data_head.list);
+	/* 
+	 * packets are of fixed sizes so initialize 
+	 * the length to rbu_data.packetsize
+	 */
+	newpacket->length = rbu_data.packetsize;
+	
+	pr_debug("create_packet: exit \n");
+
+	return 0;
+}
+
+
+static int packetize_data(void *data, size_t length) 
+{
+	int rc = 0;
+
+	pr_debug("packetize_data : entry\n");
+	if (rbu_data.packet_write_count == 0) {
+		/* create a new packet */
+		if ((rc = create_packet(length)) != 0 )
+			return rc;
+	}
+	/* fill data in to the packet */	
+	if ((rc = fill_last_packet(data, length)) != 0)
+		return rc;
+	
+	pr_debug("packetize_data : exit\n");
+	return rc;
+}
+
+
+/*
+ do_packet_read :
+ This is a helper function which reads the packet data of the 
+ current list.
+ data: is the incoming buffer
+ ptemp_list: points to the incoming list item
+ length: is the length of the free space in the buffer.
+ bytes_read: is the total number of bytes read already from 
+ the packet list
+ list_read_count: is the counter to keep track of the number 
+ of bytes read out of each packet.
+*/
+int do_packet_read(char *data, 
+		   struct list_head *ptemp_list, 
+		   int length,
+		   int bytes_read, 
+		   int *list_read_count)
+{
+	void *ptemp_buf;
+	struct packet_data *newpacket = NULL;
+	int bytes_copied = 0;
+	int j = 0;
+
+	newpacket = list_entry(ptemp_list,struct packet_data, list);
+	*list_read_count += newpacket->length;
+
+	if (*list_read_count > bytes_read) {
+		/* point to the start of unread data */
+		j = newpacket->length - (*list_read_count - bytes_read);
+		/* point to the offset in the packet buffer*/
+		ptemp_buf = (u8 *)newpacket->data + j;
+		/* check if there is enough room in the	incoming buffer*/
+		if (length > (*list_read_count - bytes_read)) 
+			/* copy what ever is there in this packet and move on*/
+			bytes_copied = (*list_read_count - bytes_read);
+		else 
+			/* copy the remaining */
+			bytes_copied = length;
+		memcpy(data, ptemp_buf, bytes_copied);
+	} 
+	return bytes_copied;
+}
+
+/*
+ packet_read_list:
+ This function reads the data out of the packet link list.
+ It will read data from multiple packets depending upon the 
+ size of the incoming buffer.
+ data: is the incoming buffer pointer
+ *pread_length: is the length of the incoming buffer. At return 
+ this value is adjusted to the actual size of the data read.
+*/
+static int packet_read_list(char *data, size_t *pread_length)
+{
+	struct list_head *ptemp_list;
+	int temp_count = 0;
+	int bytes_copied = 0;
+	int bytes_read = 0;
+	int remaining_bytes =0;
+	char *pdest = data;
+
+	/* check if we have any packets */
+	if (0 == rbu_data.num_packets)
+		return -ENOMEM;
+
+	remaining_bytes = *pread_length;
+	/* get the current read count */
+	bytes_read = rbu_data.packet_read_count;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while(!list_empty(ptemp_list)) {
+		/* read data */
+		bytes_copied = do_packet_read(pdest, ptemp_list, remaining_bytes, 
+			bytes_read, &temp_count);
+		/* adjust the remaining bytes */
+		remaining_bytes -= bytes_copied;
+		bytes_read += bytes_copied;
+		/* adjust the data pointer */
+		pdest += bytes_copied;
+
+		/* check if we reached end of buffer before reaching the last packet*/
+		if (remaining_bytes == 0)
+			break;
+
+		/* point to the next packet in the list */
+		ptemp_list = ptemp_list->next;
+	}
+	/*finally set the bytes read */
+	*pread_length = bytes_read - rbu_data.packet_read_count;
+	rbu_data.packet_read_count = bytes_read;
+	return 0;
+}
+
+static void packet_empty_list(void)
+{
+	struct list_head *ptemp_list;
+	struct list_head *pnext_list;
+	struct packet_data *newpacket;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while(!list_empty(ptemp_list)) {
+		newpacket = list_entry(ptemp_list, struct packet_data, list);
+		/* get the next list ptr before we delete this entry */
+		pnext_list = ptemp_list->next;
+		/* remove the list entry */
+		list_del(ptemp_list);
+		/* set the list to next */
+		ptemp_list = pnext_list;
+		/* zero out the RBU packet memory before freeing */
+		memset(newpacket->data, 0, rbu_data.packetsize);
+		/* free the memory pointed by this packet */
+		free_pages((unsigned long)newpacket->data, newpacket->ordernum);
+		/* now free the packet*/
+		kfree(newpacket);
+	}
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+}
+
+
+/*
+ img_update_free:
+ Frees the buffer allocated for storing BIOS image
+ Always called with lock held and returned with lock held
+*/
+static void img_update_free( void)
+{
+	if (rbu_data.image_update_buffer == NULL)
+		return;
+	
+	/* zero out this buffer before freeing it */
+	memset(rbu_data.image_update_buffer, 0, rbu_data.image_update_buffer_size);
+ 	free_pages((unsigned long)rbu_data.image_update_buffer, 
+		rbu_data.image_update_order_number);
+	/* Re-initialize the rbu_data variables after a free */
+	rbu_data.image_update_buffer = NULL;
+	rbu_data.image_update_buffer_size = 0;
+	rbu_data.bios_image_size = 0;
+}
+
+/*
+ img_update_realloc:
+ This function allocates the contiguous pages to accomodate the requested
+ size of data. The memory address and size values are stored globally and 
+ on every call to this function the new size is checked to see if more 
+ data is required than the existing size. If true the previous memory is freed
+ and new allocation is done to accomodate the new size. If the incoming size is 
+ less then than the already allocated size, then that  memory is reused.
+ This function is called with lock held and returna with lock held.
+*/
+static int img_update_realloc(unsigned long size)
+{
+	unsigned char *image_update_buffer = NULL;
+	unsigned long rc;
+	int ordernum =0;
+
+
+	/* check if the buffer of sufficient size has been already allocated */
+    if (rbu_data.image_update_buffer_size >= size) {
+		/* check for corruption */
+		if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {
+			pr_debug("img_update_realloc: corruption check failed\n");
+			return -EINVAL;
+		}
+		/* we have a valid pre-allocated buffer with sufficient size */
+		return 0;
+    }
+
+	/* free any previously allocated buffer */
+	img_update_free();
+
+	/* This has already been called as locked so we can now unlock 
+	and proceed to calling get_free_pages_limited as this function
+	can sleep*/
+	spin_unlock(&rbu_data.lock);
+
+	image_update_buffer = (unsigned char *)get_free_pages_limited(size,
+		&ordernum,
+		BIOS_SCAN_LIMIT);
+	
+	/* acquire the spinlock again */
+	spin_lock(&rbu_data.lock);
+
+	if (image_update_buffer != NULL) {
+		/* store address for the new buffer */
+		rbu_data.image_update_buffer = image_update_buffer;
+		/* adjust allocated size */
+		rbu_data.image_update_buffer_size = PAGE_SIZE << ordernum;
+		/* save the current order number */
+		rbu_data.image_update_order_number = ordernum;
+		/* initialize the new buffer data to 0 */
+		memset(rbu_data.image_update_buffer,0, rbu_data.image_update_buffer_size);
+		pr_debug("img_update_realloc: success\n");
+		/* success */
+		rc = 0; 
+	} else {
+		pr_debug("Not enough memory for image update:order number = %d"
+			",size = %ld\n",ordernum, size);
+		rc = -ENOMEM;
+	}
+
+	return rc;
+} /* img_update_realloc */
+
+
+/*
+ read_packet_data_size:
+ Returns the size of an RBU packet; if no packets present returns 0
+*/
+static ssize_t read_packet_data_size(struct kobject *kobj, 
+				     char *buffer,
+   				     loff_t pos, 
+				     size_t count)
+{
+	unsigned int size = 0;
+	if (pos == 0)
+		size = sprintf(buffer, "%lu\n", rbu_data.packetsize);
+	return size;
+} 
+
+/*
+ write_packet_data_size:
+ Writes the RBU data size supplied by the user, if the 
+ data size supplied is non zero number, this function 
+ records the packet size for any packet allocations.
+ If a byte size of zero is supplied this function will free
+ the previously allocated packets.
+*/
+static ssize_t write_packet_data_size(struct kobject *kobj, 
+				      char *buffer, 
+				      loff_t pos, 
+				      size_t count)
+{
+	int retval = count;
+
+	spin_lock(&rbu_data.lock);
+	/* extract the image size */
+	sscanf(buffer, "%lu",&rbu_data.packetsize);
+	/* free the previous packet lists */
+	packet_empty_list();
+
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+/*
+ read_rbu_data_size:
+ Returns the size of an RBU image previously downloaded
+ if no image is downloaded the size returned is 0
+*/
+static ssize_t read_rbu_data_size(struct kobject *kobj, 
+				  char *buffer, 
+				  loff_t pos, 
+				  size_t count)
+{
+	unsigned int size = 0;
+	if (pos == 0)
+		size = sprintf(buffer, "%lu\n", rbu_data.bios_image_size);
+	return size;
+} 
+
+/*
+ write_rbu_data_size:
+ Writes the RBU data size supplied by the user, if the 
+ data size supplied is non zero number, this function 
+ allocates the contiguous physical memory pages for the 
+ supplied size , if it fails this function returns error.
+ If a byte size of zero is supplied this function will free
+ the previously allocated contiguous pages.
+*/
+static ssize_t write_rbu_data_size(struct kobject *kobj, 
+				   char *buffer, 
+				   loff_t pos, 
+				   size_t count)
+{
+	int retval = count;
+
+	spin_lock(&rbu_data.lock);
+	/* extract the image size */
+	sscanf(buffer, "%lu",&rbu_data.bios_image_size);
+
+	if (rbu_data.bios_image_size !=0 ) {
+		if (img_update_realloc(rbu_data.bios_image_size) < 0) {
+			pr_debug("write_rbu_data_size: failed to allocate mem size %lu\n", 
+				(unsigned long)rbu_data.bios_image_size);
+			rbu_data.bios_image_size = 0;
+			retval = -ENOMEM;
+		} 
+	} else {
+		pr_debug(" freeing %ld size memory \n", rbu_data.bios_image_size);
+		/* free any allocated RBU memory */
+		img_update_free();
+	}
+	
+	pr_debug("write_rbu_data_size: = %lu\n", 
+		(unsigned long)rbu_data.bios_image_size);
+
+	spin_unlock(&rbu_data.lock);
+	return retval;
+} /* write_rbu_data_size*/
+
+/*
+ read_rbu_data:
+ Reads the BIOS image file from previously allocated contiguous physical 
+ pages in to the buffer supplied in this call. 
+ The reading is done in chunks of bytes supplied in the count argument.
+ The reading stops when the total number of bytes read equals the image 
+ size given previously.
+ If image size is not specified or if the image size is zero this function 
+ returns failure.
+ Parameters: 
+ kobj is the kernel object 
+ buffer is the pointer to the incoming data buffer.
+ count is the value of the incoming buffer size,
+ pos is the amount of bytes already read.
+*/
+static ssize_t read_rbu_data(struct kobject *kobj, 
+			     char *buffer,
+			     loff_t pos, 
+			     size_t count)
+{
+	unsigned char *ptemp = NULL;
+	int retval =0;
+	size_t bytes_left = 0;
+	size_t data_length = 0;
+
+	spin_lock(&rbu_data.lock);
+
+	/* check to see if we have something to return */
+	if ((rbu_data.image_update_buffer == NULL) || 
+		(rbu_data.bios_image_size == 0)) {
+		pr_debug("read_rbu_data: image_update_buffer %p ,"
+			"bios_image_size %lu\n",
+			rbu_data.image_update_buffer, 
+			rbu_data.bios_image_size);
+		retval = -ENOMEM;
+		goto read_rbu_data_exit;
+	}
+	
+	if ( pos > rbu_data.bios_image_size ) {
+		retval = 0;
+		goto read_rbu_data_exit;
+	}
+
+	bytes_left = rbu_data.bios_image_size - pos;
+	data_length = max(bytes_left,count);
+
+	ptemp = rbu_data.image_update_buffer;
+	memcpy(buffer, (ptemp + pos), data_length);
+
+	if ((pos + count) > rbu_data.bios_image_size)
+		/* this was the last copy */
+		retval = bytes_left;
+	else 
+		retval = count;
+	
+read_rbu_data_exit:
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+/*
+ write_rbu_data
+ Writes from the incoming BIOS image file to the pre-allocated 
+ contiguous physical memory pages. 
+ The writes occur in chunks of memory supplied by the count. The writes 
+ stops when the total memory supplied equals the image size given previously.
+ If no memory size is previously specified or if the previously specified size 
+ is zero the write returns error.
+*/
+static ssize_t write_rbu_data(struct kobject *kobj, 
+			      char *buffer,
+			      loff_t pos, 
+			      size_t count)
+{
+	unsigned char *pDest = NULL;
+	unsigned char *ptemp = NULL;
+	int retval = 0;
+
+	spin_lock(&rbu_data.lock);
+
+	/* check if the image size is given */
+	if (0 == rbu_data.bios_image_size) {
+		printk(KERN_WARNING"write_rbu_data: BIOS image size not set\n");
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+
+	if ((pos + count) > rbu_data.bios_image_size) {
+		pr_debug("write_rbu_data: data_over_run, file pos %lu "
+			"bios_image_size %lu\n",
+			(unsigned long)pos,
+			rbu_data.bios_image_size);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+
+	pDest = (unsigned char*)rbu_data.image_update_buffer;
+	ptemp = pDest + pos;
+	memcpy(ptemp, buffer,  count);
+	retval = count;
+	pr_debug("write_rbu_data : retval = %d\n", retval);
+error_exit:
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+/*
+ read_rbu_packet_data:
+ Reads the BIOS image file from previously allocated contiguous physical 
+ pages in to the buffer supplied in this call. 
+ The reading is done in chunks of bytes supplied in the count argument.
+ The reading stops when the total number of bytes read equals the image 
+ size given previously.
+ If image size is not specified or if the image size is zero this function 
+ returns failure.
+ Parameters: 
+ kobj is the kernel object 
+ buffer is the pointer to the incoming data buffer.
+ count is the value of the incoming buffer size,
+ pos is the amount of bytes already read.
+*/
+static ssize_t read_rbu_packet_data(struct kobject *kobj, 
+				    char *buffer,
+				    loff_t pos, 
+				    size_t count)
+{
+	int retval;
+	size_t bytes_left;
+	size_t data_length;
+	char *ptempBuf = buffer;
+	unsigned long imagesize;
+
+	spin_lock(&rbu_data.lock);
+
+	/* check to see if we have something to return */
+	if (rbu_data.num_packets == 0) {
+		pr_debug("read_rbu_packet_data: no packets written\n");
+		retval = -ENOMEM;
+		goto read_rbu_data_exit;
+	}
+
+	imagesize = rbu_data.num_packets * rbu_data.packetsize;
+	
+	if ( pos > imagesize ) {
+		retval = 0;
+		printk(KERN_WARNING"read_rbu_packet_data: data underrun\n");
+		goto read_rbu_data_exit;
+	}
+
+	bytes_left = imagesize - pos;
+	data_length = max(bytes_left, count);
+
+	if ((retval = packet_read_list(ptempBuf, &data_length)) < 0)
+		goto read_rbu_data_exit;
+
+	if ((pos + count) > imagesize) {
+		rbu_data.packet_read_count = 0;
+		/* this was the last copy */
+		retval = bytes_left;
+	}
+	else 
+		retval = count;
+	
+read_rbu_data_exit:
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+/*
+ write_rbu_packet_data
+ Writes from the incoming BIOS image file to the pre-allocated 
+ contiguous physical memory pages. 
+ The writes occur in chunks of memory supplied by the count. The writes 
+ stops when the total memory supplied equals the image size given previously.
+ If no memory size is previously specified or if the previously specified size 
+ is zero the write returns error.
+*/
+static ssize_t write_rbu_packet_data(struct kobject *kobj, 
+				     char *buffer,
+				     loff_t pos, 
+				     size_t count)
+{
+	int retval = 0;
+
+	spin_lock(&rbu_data.lock);
+
+	/* check if the packet size is given */
+	if (0 == rbu_data.packetsize) {
+		printk(KERN_WARNING"write_rbu_packet_data: packetsize not set\n");
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+
+	if ((pos + count) > rbu_data.packetsize) {
+		pr_debug("write_rbu_packet_data: data_over_run, file pos %lu,"
+			"packetsize %lu\n",
+			(unsigned long)pos,
+			rbu_data.packetsize);
+		retval = -ENOMEM;
+
+		/* We have a write data overrun, obviously this is 
+		not the corret file, so free the previous data*/
+		pr_debug("data overrun freeing all the previous packets\n");
+		packet_empty_list();
+		goto error_exit;
+	}
+
+	if ((retval = packetize_data(buffer, count)) < 0 ) {
+		pr_debug(KERN_WARNING"write_rbu_packet_data: packetize_data "
+			"failed with status %d\n", retval);
+		retval = -EIO;
+		goto error_exit;
+	} 
+
+	retval = count;
+
+	pr_debug("write_rbu_packet_data : retval = %d\n", retval);
+
+error_exit:
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+
+static struct bin_attribute rbudata_attr = {
+	.attr = {.name = "rbudata", .owner = THIS_MODULE, .mode = 0644},
+	.read = read_rbu_data,
+	.write = write_rbu_data,
+};
+
+static struct bin_attribute rbudatasize_attr = {
+	.attr = { .name = "rbudatasize", .owner = THIS_MODULE, .mode = 0644 },
+	.read = read_rbu_data_size,
+	.write= write_rbu_data_size,
+};
+
+static struct bin_attribute packetdatasize_attr = {
+	.attr = { .name = "packetdatasize", .owner = THIS_MODULE, .mode = 0644 },
+	.read = read_packet_data_size,
+	.write= write_packet_data_size,
+};
+
+static struct bin_attribute packetdata_attr = {
+	.attr = { .name = "packetdata", .owner = THIS_MODULE, .mode = 0644 },
+	.read = read_rbu_packet_data,
+	.write= write_rbu_packet_data,
+};
+
+static int __init dcdrbu_init(void)
+{
+	int rc;
+
+	spin_lock_init(&rbu_data.lock);
+
+	init_packet_head();
+	
+	rc = firmware_register(&rbu_subsys);
+	if (rc < 0) {
+		printk(KERN_WARNING"dcdrbu_init: firmware_register failed\n");
+		return rc;
+	}
+
+	sysfs_create_bin_file(&rbu_subsys.kset.kobj,&rbudata_attr);
+	sysfs_create_bin_file(&rbu_subsys.kset.kobj,&rbudatasize_attr);
+	sysfs_create_bin_file(&rbu_subsys.kset.kobj,&packetdatasize_attr);
+	sysfs_create_bin_file(&rbu_subsys.kset.kobj,&packetdata_attr);
+
+	return rc;
+}
+
+static __exit void dcdrbu_exit( void)
+{
+	spin_lock(&rbu_data.lock);
+	packet_empty_list();
+	img_update_free();
+	spin_unlock(&rbu_data.lock);
+	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &rbudata_attr );
+	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &rbudatasize_attr );
+	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &packetdatasize_attr );
+	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &packetdata_attr );
+	firmware_unregister(&rbu_subsys);
+} 
+
+module_exit(dcdrbu_exit);
+module_init(dcdrbu_init);
+
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Kconfig linux-2.6.11.8/drivers/firmware/Kconfig
--- linux-2.6.11.8.ORIG/drivers/firmware/Kconfig	2005-05-13 12:07:58.965181016 -0500
+++ linux-2.6.11.8/drivers/firmware/Kconfig	2005-05-13 12:07:00.566059032 -0500
@@ -58,4 +58,16 @@ config EFI_PCDP
 
 	  See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf>
 
+config DELL_RBU
+        tristate "BIOS update support for DELL systems via sysfs"
+        default n
+        help
+          Say Y if you want to have the option of updating the BIOS for your
+	  DELL system. Note you need a supporting application to comunicate 
+	  with the BIOS regardign the new image for the image update to 
+	  take effect.
+
+	  See <file:Documentation/DELL_RBU.txt> for more details on the driver.
+
+
 endmenu
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Makefile linux-2.6.11.8/drivers/firmware/Makefile
--- linux-2.6.11.8.ORIG/drivers/firmware/Makefile	2005-05-13 12:08:12.839071864 -0500
+++ linux-2.6.11.8/drivers/firmware/Makefile	2005-05-09 15:15:16.000000000 -0500
@@ -4,3 +4,4 @@
 obj-$(CONFIG_EDD)             	+= edd.o
 obj-$(CONFIG_EFI_VARS)		+= efivars.o
 obj-$(CONFIG_EFI_PCDP)		+= pcdp.o
+obj-$(CONFIG_DELL_RBU)		+= dell_rbu.o

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-13 19:00 [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver Abhay Salunke
@ 2005-05-13 21:11 ` Alexey Dobriyan
  0 siblings, 0 replies; 28+ messages in thread
From: Alexey Dobriyan @ 2005-05-13 21:11 UTC (permalink / raw)
  To: Abhay Salunke; +Cc: linux-kernel, Andrew Morton, matt_domsch

On Friday 13 May 2005 23:00, Abhay Salunke wrote:
--- linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt
+++ linux-2.6.11.8/Documentation/DELL_RBU.txt

> +physical memory of packetdatasize and the data is written to that meomry.
								     ^^^^^^
> +Afte updating the BIOS image the appplication needs to communicate with the BIOS 
   ^^^^
> +reboot the system imideately or not reboot the system and leave up to the user 
		     ^^^^^^^^^^

--- linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c
+++ linux-2.6.11.8/drivers/firmware/dell_rbu.c

> + * Changelog:
> + * 
> + * 13 May 2005 Abhay Salunke <Abhay_Salunke@dell.com>
> + * Modified code with suggestions from Andrew Morton; 

Maintaining ChangeLogs is the job of SCM. At least don't put my name here. ;-)

> +void init_packet_head(void)

static?

> +static int fill_last_packet(void *data, size_t length)

> +	memcpy(((char *)ppacket->data + rbu_data.packet_write_count), 

No need for cast and brackets.

> +		data, length);

> +		/* adjust the total packet length */

Useless comment.

> +		rbu_data.packet_write_count += length;

> +static void *get_free_pages_limited(unsigned long size,

> +		/* The incoming size is very large */

Useless comment.

> +		pr_debug("get_free_pages_limited: Incoming size is very large\n");

> +	pbuf =(unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);

pbuf is void *.

> +		img_buf_phys_addr = (unsigned long)virt_to_phys((void *)pbuf);

pbuf is void *.

> +			pbuf =(unsigned char *)__get_free_pages(GFP_DMA, *ordernum);

pbuf is void *.

> +static int create_packet(size_t length)

> +	newpacket = kmalloc(sizeof(struct packet_data) ,GFP_KERNEL);
> +	if(newpacket == NULL) {
> +		printk(KERN_WARNING"create_packet: failed to allocate new packet\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* there is no upper limit on memory address for packetized mechanism */
> +	newpacket->data = get_free_pages_limited(rbu_data.packetsize,&ordernum,	0);
> +	pr_debug("create_packet: newpacket %p\n", newpacket->data);
> +		
> +	if(newpacket->data == NULL) {
> +		printk(KERN_WARNING"create_packet: failed to allocate new packet\n");
> +		return -ENOMEM;

You leak newpacket.

> +	/* initialize the newly created packet headers */

Useless comment.

> +	INIT_LIST_HEAD(&newpacket->list);
> +	/* add this packet to the link list */

Useless comment.

> +	list_add_tail(&newpacket->list, &packet_data_head.list);
> +	/* 
> +	 * packets are of fixed sizes so initialize 
> +	 * the length to rbu_data.packetsize
> +	 */

"Packets have fixed size" is enough.

> +	newpacket->length = rbu_data.packetsize;

> +static int packetize_data(void *data, size_t length) 

> +		/* create a new packet */

Useless comment.

> +		if ((rc = create_packet(length)) != 0 )
> +			return rc;

> +static int packet_read_list(char *data, size_t *pread_length)

> +	/* get the current read count */

Useless comment.

> +	bytes_read = rbu_data.packet_read_count;

> +	ptemp_list = (&packet_data_head.list)->next;
> +	while(!list_empty(ptemp_list)) {

> +		/* adjust the remaining bytes */

Useless comment.

> +		remaining_bytes -= bytes_copied;
> +		bytes_read += bytes_copied;
> +		/* adjust the data pointer */

Useless comment.

> +		pdest += bytes_copied;

> +		/* point to the next packet in the list */

Useless comment.

> +		ptemp_list = ptemp_list->next;

> +static void packet_empty_list(void)

> +	ptemp_list = (&packet_data_head.list)->next;
> +	while(!list_empty(ptemp_list)) {
> +		newpacket = list_entry(ptemp_list, struct packet_data, list);
> +		/* get the next list ptr before we delete this entry */

Useless comment.

> +		pnext_list = ptemp_list->next;
> +		/* remove the list entry */

Useless comment.

> +		list_del(ptemp_list);
> +		/* set the list to next */

Useless comment.

> +		ptemp_list = pnext_list;
> +		/* zero out the RBU packet memory before freeing */
> +		memset(newpacket->data, 0, rbu_data.packetsize);

For what? Useless comment, anyway.

> +		/* free the memory pointed by this packet */

Useless comment.

> +		free_pages((unsigned long)newpacket->data, newpacket->ordernum);
> +		/* now free the packet*/

Useless comment.

> +		kfree(newpacket);

> +static void img_update_free( void)
			       ^
> +	/* zero out this buffer before freeing it */
> +	memset(rbu_data.image_update_buffer, 0, rbu_data.image_update_buffer_size);

For what?

> + 	free_pages((unsigned long)rbu_data.image_update_buffer, 
> +		rbu_data.image_update_order_number);

> +static int img_update_realloc(unsigned long size)

> +	if (image_update_buffer != NULL) {
> +		/* store address for the new buffer */

Useless comment.

> +		rbu_data.image_update_buffer = image_update_buffer;
> +		/* adjust allocated size */

Useless comment.

> +		rbu_data.image_update_buffer_size = PAGE_SIZE << ordernum;
> +		/* save the current order number */

Useless comment.

> +		rbu_data.image_update_order_number = ordernum;
> +		/* initialize the new buffer data to 0 */

Useless comment.

> +		memset(rbu_data.image_update_buffer,0, rbu_data.image_update_buffer_size);
> +		pr_debug("img_update_realloc: success\n");
> +		/* success */

Useless comment.

> +		rc = 0; 

> +static int __init dcdrbu_init(void)

> +		printk(KERN_WARNING"dcdrbu_init: firmware_register failed\n");

People usually leave space after KERN_*.

> +static __exit void dcdrbu_exit( void)
				  ^
> +	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &rbudata_attr );
> +	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &rbudatasize_attr );
> +	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &packetdatasize_attr );
> +	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &packetdata_attr );

Almost trailing whitespace.

--- linux-2.6.11.8.ORIG/drivers/firmware/Kconfig
+++ linux-2.6.11.8/drivers/firmware/Kconfig
> +	  DELL system. Note you need a supporting application to comunicate 
								 ^^^^^^^^^^
> +	  with the BIOS regardign the new image for the image update to 
			^^^^^^^^^

Also, try to fit both comments and code in 80 characters, run comments through
spellchecker, use consistent style for comments (
	/* foo                    /* foo
	   bar 1 */        or      * bar 1 */
) is OK.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [patch 2.6.12-rc3]dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-07-20 23:50 Abhay Salunke
  0 siblings, 0 replies; 28+ messages in thread
From: Abhay Salunke @ 2005-07-20 23:50 UTC (permalink / raw)
  To: linux-kernel, akpm, abhay_salunke; +Cc: greg

Resending dell_rbu driver after making a few more improvements and also 
using the new request_firmware_nowait kernel API sent in the firmware_class.c 
patch.

This patch has been tested on i386 and x86-64 systems along with the 
firmware_class.c patch and it works fine.

Signed-off-by: Abhay Salunke <Abhay_Salunke@dell.com>

Thanks
Abhay
diff -uprN linux-2.6.11.11.orig/Documentation/dell_rbu.txt linux-2.6.11.11.new/Documentation/dell_rbu.txt
--- linux-2.6.11.11.orig/Documentation/dell_rbu.txt	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.11.new/Documentation/dell_rbu.txt	2005-07-14 14:59:10.000000000 -0500
@@ -0,0 +1,74 @@
+Purpose:
+Demonstrate the usage of the new open sourced rbu (Remote BIOS Update) driver
+for updating BIOS images on Dell servers and desktops.
+
+Scope:
+This document discusses the functionality of the rbu driver only.
+It does not cover the support needed from aplications to enable the BIOS to
+update itself with the image downloaded in to the memory.
+
+Overview:
+This driver works with Dell OpenManage or Dell Update Packages for updating 
+the BIOS on Dell servers (starting from servers sold since 1999), desktops 
+and notebooks (starting from those sold in 2005). 
+Please go to  http://support.dell.com register and you can find info on
+OpenManage and Dell Update packages (DUP).
+
+Dell_RBU driver supports BIOS update using the monilothic image and packetized
+image methods. In case of moniolithic the driver allocates a contiguous chunk
+of physical pages having the BIOS image. In case of packetized the app
+using the driver breaks the image in to packets of fixed sizes and the driver
+would place each packet in contiguous physical memory. The driver also
+maintains a link list of packets for reading them back.
+If the dell_rbu driver is unloaded all the allocated memory is freed.
+
+The rbu driver needs to have an application which will inform the BIOS to
+enable the update in the next system reboot.
+
+The user should not unload the rbu driver after downloading the BIOS image
+or updating.
+
+The driver load creates the following directories under the /sys file system.
+/sys/class/firmware/dell_rbu/loading 
+/sys/class/firmware/dell_rbu/data
+/sys/devices/platform/dell_rbu/image_type
+/sys/devices/platform/dell_rbu/data
+
+The driver supports two types of update mechanism; monolithic and packetized.
+These update mechanism depends upon the BIOS currently running on the system.
+Most of the Dell systems support a monolithic update where the BIOS image is 
+copied to a single contiguous block of physical memory. 
+In case of packet mechanism the single memory can be broken in smaller chuks
+of contiguous memory and the BIOS image is scattered in these packets.
+
+By default the driver uses monolithic memory for the update type. This can be
+changed to contiguous during the driver load time by specifying the load
+parameter image_type=packet.  This can also be changed later as below
+echo "packet" > /sys/devices/platform/dell_rbu/image_type 
+
+Do the steps below to download the BIOS image.
+1) echo 1 > /sys/class/firmware/dell_rbu/loading
+2) cp bios_image.hdr /sys/class/firmware/dell_rbu/data
+3) echo 0 > /sys/class/firmware/dell_rbu/loading
+
+The /sys/class/firmware/dell_rbu/ entries will remain till the following is
+done. 
+echo -1 > /sys/class/firmware/dell_rbu/loading
+
+Until this step is completed the drivr cannot be unloaded.
+
+Also the driver provides /sys/devices/platform/dell_rbu/data readonly file to
+read back the image downloaded. This is useful in case of packet update 
+mechanism where the above steps 1,2,3 will repeated for every packet. 
+By reading the /sys/devices/platform/dell_rbu/data file all packet data 
+downloaded can be verified in a single file. 
+The packets are arranged in this file one after the other in a FIFO order.
+
+NOTE:
+This driver requires a patch for firmware_class.c which has the addition
+of request_firmware_nowait_nohotplug function to wortk
+Also after updating the BIOS image an user mdoe application neeeds to execute
+code which message the BIOS update request to the BIOS. So on the next reboot
+the BIOS knows about the new image downloaded and it updates it self.
+Also don't unload the rbu drive if the image has to be updated.
+
diff -uprN linux-2.6.11.11.orig/drivers/firmware/dell_rbu.c linux-2.6.11.11.new/drivers/firmware/dell_rbu.c
--- linux-2.6.11.11.orig/drivers/firmware/dell_rbu.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.11.new/drivers/firmware/dell_rbu.c	2005-07-20 18:30:51.000000000 -0500
@@ -0,0 +1,656 @@
+/*
+ * dell_rbu.c
+ * Bios Update driver for Dell systems
+ * Author: Dell Inc
+ *         Abhay Salunke <abhay_salunke@dell.com>
+ *
+ * Copyright (C) 2005 Dell Inc.
+ *
+ * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by 
+ * creating entries in the /sys file systems on Linux 2.6 and higher 
+ * kernels. The driver supports two mechanism to update the BIOS namely 
+ * contiguous and packetized. Both these methods still require having some
+ * application to set the CMOS bit indicating the BIOS to update itself 
+ * after a reboot.
+ *
+ * Contiguous method:
+ * This driver writes the incoming data in a monolithic image by allocating 
+ * contiguous physical pages large enough to accommodate the incoming BIOS 
+ * image size.  
+ *
+ * Packetized method:
+ * The driver writes the incoming packet image by allocating a new packet 
+ * on every time the packet data is written. This driver requires an 
+ * application to break the BIOS image in to fixed sized packet chunks.
+ *
+ * See Documentation/dell_rbu.txt for more info.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation
+ *
+ * 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.
+ */
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+
+MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
+MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+#define BIOS_SCAN_LIMIT 0xffffffff
+#define MAX_IMAGE_LENGTH 16
+static struct _rbu_data {
+	void *image_update_buffer;
+	unsigned long image_update_buffer_size;
+	unsigned long bios_image_size;
+	int image_update_ordernum;
+	int dma_alloc;
+	spinlock_t lock;
+	unsigned long packet_read_count;
+	unsigned long packet_write_count;
+	unsigned long num_packets;
+	unsigned long packetsize;
+} rbu_data;
+
+static char image_type[MAX_IMAGE_LENGTH] = "mono";
+module_param_string(image_type, image_type, sizeof (image_type), 0);
+MODULE_PARM_DESC(image_type, "BIOS image type. choose- mono or packet");
+
+struct packet_data {
+	struct list_head list;
+	size_t length;
+	void *data;
+	int ordernum;
+};
+
+static struct packet_data packet_data_head;
+
+static struct platform_device *rbu_device;
+static int context;
+static dma_addr_t dell_rbu_dmaaddr;
+
+static void
+init_packet_head(void)
+{
+	INIT_LIST_HEAD(&packet_data_head.list);
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+static int
+fill_last_packet(void *data, size_t length)
+{
+	struct list_head *ptemp_list;
+	struct packet_data *packet = NULL;
+	int packet_count = 0;
+
+	pr_debug("fill_last_packet: entry \n");
+
+	if (!rbu_data.num_packets) {
+		pr_debug("fill_last_packet: num_packets=0\n");
+		return -ENOMEM;
+	}
+
+	packet_count = rbu_data.num_packets;
+
+	ptemp_list = (&packet_data_head.list)->prev;
+
+	packet = list_entry(ptemp_list, struct packet_data, list);
+
+	if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
+		pr_debug("dell_rbu:%s: packet size data "
+			"overrun\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	pr_debug("fill_last_packet : buffer = %p\n", packet->data);
+
+	memcpy((packet->data + rbu_data.packet_write_count), data, length);
+
+	if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
+		/*
+		 * this was the last data chunk in the packet
+		 * so reinitialize the packet data counter to zero
+		 */
+		rbu_data.packet_write_count = 0;
+	} else
+		rbu_data.packet_write_count += length;
+
+	pr_debug("fill_last_packet: exit \n");
+	return 0;
+}
+
+static int
+create_packet(size_t length)
+{
+	struct packet_data *newpacket;
+	int ordernum = 0;
+
+	pr_debug("create_packet: entry \n");
+
+	if (!rbu_data.packetsize) {
+		pr_debug("create_packet: packetsize not specified\n");
+		return -EINVAL;
+	}
+
+	newpacket = kmalloc(sizeof (struct packet_data), GFP_KERNEL);
+	if (!newpacket) {
+		printk(KERN_WARNING
+			"dell_rbu:%s: failed to allocate new "
+			"packet\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	ordernum = get_order(length);
+	/*
+	 * there is no upper limit on memory 
+	 * address for packetized mechanism 
+	 */
+	newpacket->data = (unsigned char *) __get_free_pages(GFP_KERNEL,
+		ordernum);
+
+	pr_debug("create_packet: newpacket %p\n", newpacket->data);
+
+	if (!newpacket->data) {
+		printk(KERN_WARNING
+			"dell_rbu:%s: failed to allocate new "
+			"packet\n", __FUNCTION__);
+		kfree(newpacket);
+		return -ENOMEM;
+	}
+
+	newpacket->ordernum = ordernum;
+	++rbu_data.num_packets;
+	/*
+	 * initialize the newly created packet headers 
+	 */
+	INIT_LIST_HEAD(&newpacket->list);
+	list_add_tail(&newpacket->list, &packet_data_head.list);
+	/*
+	 * packets have fixed size 
+	 */
+	newpacket->length = rbu_data.packetsize;
+
+	pr_debug("create_packet: exit \n");
+
+	return 0;
+}
+
+static int
+packetize_data(void *data, size_t length)
+{
+	int rc = 0;
+
+	if (!rbu_data.packet_write_count) {
+		if ((rc = create_packet(length)))
+			return rc;
+	}
+	if ((rc = fill_last_packet(data, length)))
+		return rc;
+
+	return rc;
+}
+
+static int
+do_packet_read(char *data, struct list_head *ptemp_list,
+	int length, int bytes_read, int *list_read_count)
+{
+	void *ptemp_buf;
+	struct packet_data *newpacket = NULL;
+	int bytes_copied = 0;
+	int j = 0;
+
+	newpacket = list_entry(ptemp_list, struct packet_data, list);
+	*list_read_count += newpacket->length;
+
+	if (*list_read_count > bytes_read) {
+		/* point to the start of unread data */
+		j = newpacket->length - (*list_read_count - bytes_read);
+		/* point to the offset in the packet buffer */
+		ptemp_buf = (u8 *) newpacket->data + j;
+		/* 
+		 * check if there is enough room in 
+		 * * the incoming buffer 
+		 */
+		if (length > (*list_read_count - bytes_read))
+			/* 
+			 * copy what ever is there in this 
+			 * packet and move on 
+			 */
+			bytes_copied = (*list_read_count - bytes_read);
+		else
+			/* copy the remaining */
+			bytes_copied = length;
+		memcpy(data, ptemp_buf, bytes_copied);
+	}
+	return bytes_copied;
+}
+
+static int
+packet_read_list(char *data, size_t * pread_length)
+{
+	struct list_head *ptemp_list;
+	int temp_count = 0;
+	int bytes_copied = 0;
+	int bytes_read = 0;
+	int remaining_bytes = 0;
+	char *pdest = data;
+
+	/* check if we have any packets */
+	if (0 == rbu_data.num_packets)
+		return -ENOMEM;
+
+	remaining_bytes = *pread_length;
+	bytes_read = rbu_data.packet_read_count;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while (!list_empty(ptemp_list)) {
+		bytes_copied = do_packet_read(pdest, ptemp_list,
+			remaining_bytes, bytes_read, &temp_count);
+		remaining_bytes -= bytes_copied;
+		bytes_read += bytes_copied;
+		pdest += bytes_copied;
+		/*
+		 * check if we reached end of buffer before reaching the
+		 * last packet
+		 */
+		if (remaining_bytes == 0)
+			break;
+
+		ptemp_list = ptemp_list->next;
+	}
+	/*finally set the bytes read */
+	*pread_length = bytes_read - rbu_data.packet_read_count;
+	rbu_data.packet_read_count = bytes_read;
+	return 0;
+}
+
+static void
+packet_empty_list(void)
+{
+	struct list_head *ptemp_list;
+	struct list_head *pnext_list;
+	struct packet_data *newpacket;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while (!list_empty(ptemp_list)) {
+		newpacket =
+			list_entry(ptemp_list, struct packet_data, list);
+		pnext_list = ptemp_list->next;
+		list_del(ptemp_list);
+		ptemp_list = pnext_list;
+		/*
+		 * zero out the RBU packet memory before freeing 
+		 * to make sure there are no stale RBU packets left in memory
+		 */
+		memset(newpacket->data, 0, rbu_data.packetsize);
+		free_pages((unsigned long) newpacket->data,
+			newpacket->ordernum);
+		kfree(newpacket);
+	}
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+/*
+ * img_update_free: Frees the buffer allocated for storing BIOS image
+ * Always called with lock held and returned with lock held 
+ */
+static void
+img_update_free(void)
+{
+	if (!rbu_data.image_update_buffer)
+		return;
+	/*
+	 * zero out this buffer before freeing it to get rid of any stale
+	 * BIOS image copied in memory.
+	 */
+	memset(rbu_data.image_update_buffer, 0,
+		rbu_data.image_update_buffer_size);
+	if (rbu_data.dma_alloc == 1)
+		dma_free_coherent(NULL, rbu_data.bios_image_size,
+			rbu_data.image_update_buffer, dell_rbu_dmaaddr);
+	else
+		free_pages((unsigned long) rbu_data.image_update_buffer,
+			rbu_data.image_update_ordernum);
+
+	/*
+	 * Re-initialize the rbu_data variables after a free 
+	 */
+	rbu_data.image_update_ordernum = -1;
+	rbu_data.image_update_buffer = NULL;
+	rbu_data.image_update_buffer_size = 0;
+	rbu_data.bios_image_size = 0;
+	rbu_data.dma_alloc = 0;
+}
+
+/*
+ * img_update_realloc: This function allocates the contiguous pages to
+ * accommodate the requested size of data. The memory address and size
+ * values are stored globally and on every call to this function the new
+ * size is checked to see if more data is required than the existing size. 
+ * If true the previous memory is freed and new allocation is done to
+ * accommodate the new size. If the incoming size is less then than the
+ * already allocated size, then that memory is reused. This function is
+ * called with lock held and returns with lock held. 
+ */
+static int
+img_update_realloc(unsigned long size)
+{
+	unsigned char *image_update_buffer = NULL;
+	unsigned long rc;
+	unsigned long img_buf_phys_addr;
+	int ordernum;
+	int dma_alloc = 0; 
+
+	/*
+	 * check if the buffer of sufficient size has been 
+	 * already allocated 
+	 */
+	if (rbu_data.image_update_buffer_size >= size) {
+		/*
+		 * check for corruption 
+		 */
+		if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {
+			printk(KERN_ERR "dell_rbu:%s: corruption "
+				"check failed\n", __FUNCTION__);
+			return -EINVAL;
+		}
+		/*
+		 * we have a valid pre-allocated buffer with 
+		 * sufficient size 
+		 */
+		return 0;
+	}
+
+	/*
+	 * free any previously allocated buffer 
+	 */
+	img_update_free();
+	
+	spin_unlock(&rbu_data.lock);
+	
+	ordernum = get_order(size);
+	image_update_buffer =
+		(unsigned char *) __get_free_pages(GFP_KERNEL,
+		ordernum);
+
+	img_buf_phys_addr =
+		(unsigned long) virt_to_phys(image_update_buffer);
+	
+	if (img_buf_phys_addr > BIOS_SCAN_LIMIT) {
+		free_pages((unsigned long) image_update_buffer,
+			ordernum);
+		ordernum = -1;
+		image_update_buffer = dma_alloc_coherent(NULL, size,
+			&dell_rbu_dmaaddr, GFP_KERNEL);
+		dma_alloc = 1;
+	}
+	
+	spin_lock(&rbu_data.lock);
+	
+	if (image_update_buffer != NULL) {
+		rbu_data.image_update_buffer = image_update_buffer;
+		rbu_data.image_update_buffer_size = size;
+		rbu_data.bios_image_size =
+			rbu_data.image_update_buffer_size;
+		rbu_data.image_update_ordernum = ordernum;
+		rbu_data.dma_alloc = dma_alloc;
+		rc = 0;
+	} else {
+		pr_debug("Not enough memory for image update:"
+			"size = %ld\n", size);
+		rc = -ENOMEM;
+	}
+
+	return rc;
+}
+
+static ssize_t
+read_packet_data(char *buffer, loff_t pos, size_t count)
+{
+	int retval;
+	size_t bytes_left;
+	size_t data_length;
+	char *ptempBuf = buffer;
+	unsigned long imagesize;
+
+	/* check to see if we have something to return */
+	if (rbu_data.num_packets == 0) {
+		pr_debug("read_packet_data: no packets written\n");
+		retval = -ENOMEM;
+		goto read_rbu_data_exit;
+	}
+
+	imagesize = rbu_data.num_packets * rbu_data.packetsize;
+
+	if (pos > imagesize) {
+		retval = 0;
+		printk(KERN_WARNING "dell_rbu:read_packet_data: "
+			"data underrun\n");
+		goto read_rbu_data_exit;
+	}
+
+	bytes_left = imagesize - pos;
+	data_length = min(bytes_left, count);
+
+	if ((retval = packet_read_list(ptempBuf, &data_length)) < 0)
+		goto read_rbu_data_exit;
+
+	if ((pos + count) > imagesize) {
+		rbu_data.packet_read_count = 0;
+		/* this was the last copy */
+		retval = bytes_left;
+	} else
+		retval = count;
+
+read_rbu_data_exit:
+	return retval;
+}
+
+static ssize_t
+read_rbu_mono_data(char *buffer, loff_t pos, size_t count)
+{
+	unsigned char *ptemp = NULL;
+	size_t bytes_left = 0;
+	size_t data_length = 0;
+	ssize_t ret_count = 0;
+
+	/* check to see if we have something to return */
+	if ((rbu_data.image_update_buffer == NULL) ||
+		(rbu_data.bios_image_size == 0)) {
+		pr_debug("read_rbu_data_mono: image_update_buffer %p ,"
+			"bios_image_size %lu\n",
+			rbu_data.image_update_buffer,
+			rbu_data.bios_image_size);
+		ret_count = -ENOMEM;
+		goto read_rbu_data_exit;
+	}
+
+	if (pos > rbu_data.bios_image_size) {
+		ret_count = 0;
+		goto read_rbu_data_exit;
+	}
+
+	bytes_left = rbu_data.bios_image_size - pos;
+	data_length = min(bytes_left, count);
+
+	ptemp = rbu_data.image_update_buffer;
+	memcpy(buffer, (ptemp + pos), data_length);
+
+	if ((pos + count) > rbu_data.bios_image_size)
+		/* this was the last copy */
+		ret_count = bytes_left;
+	else
+		ret_count = count;
+read_rbu_data_exit:
+	return ret_count;
+}
+
+static ssize_t
+read_rbu_data(struct kobject *kobj, char *buffer, loff_t pos, size_t count)
+{
+	ssize_t ret_count = 0;
+
+	spin_lock(&rbu_data.lock);
+
+	if (!strcmp(image_type, "mono"))
+		ret_count = read_rbu_mono_data(buffer, pos, count);
+	else if (!strcmp(image_type, "packet"))
+		ret_count = read_packet_data(buffer, pos, count);
+	else
+		pr_debug("read_rbu_data: invalid image type specified\n");
+
+	spin_unlock(&rbu_data.lock);
+	return ret_count;
+}
+
+static ssize_t
+rbu_show_image_type(struct platform_device *rbu_dev, char *buf)
+{
+	unsigned int size = 0;
+	size = sprintf(buf, "%s\n", image_type);
+	return size;
+}
+
+static ssize_t
+rbu_store_image_type(struct platform_device *rbu_dev,
+	const char *buf, size_t count)
+{
+	int rc = count;
+	spin_lock(&rbu_data.lock);
+
+	if (strlen(buf) < MAX_IMAGE_LENGTH) {
+		if (strstr(buf, "mono") || strstr(buf, "packet"))
+			sscanf(buf, "%s", image_type);
+	}
+	else
+		printk(KERN_WARNING "dell_rbu: image_type is invalid"
+			"max chars = %d\n", MAX_IMAGE_LENGTH);
+
+	/* we must free all previous allocations */
+	packet_empty_list();
+	img_update_free();
+
+	spin_unlock(&rbu_data.lock);
+	return rc;
+}
+
+struct rbu_attribute {
+	struct attribute attr;
+	ssize_t(*show) (struct platform_device * rbu_dev, char *buf);
+	ssize_t(*store) (struct platform_device * rbu_dev,
+		const char *buf, size_t count);
+};
+
+#define RBU_DEVICE_ATTR(_name, _mode, _show, _store ) \
+struct rbu_attribute rbu_attr_##_name = {       \
+	.attr = {.name = __stringify(_name),  \
+			.mode= _mode, .owner= THIS_MODULE},\
+	.show = _show,                                \
+	.store = _store,                                \
+};
+
+static RBU_DEVICE_ATTR(image_type, 0644, rbu_show_image_type,
+	rbu_store_image_type);
+
+static struct bin_attribute rbu_data_attr = {
+	.attr = {.name = "data", .owner = THIS_MODULE, .mode = 0444},
+	.read = read_rbu_data,
+};
+
+static void
+callbackfn_rbu(const struct firmware *fw, void *context)
+{
+	int rc = 0;
+
+	if (!fw || !fw->size)
+		return;
+
+	spin_lock(&rbu_data.lock);
+	if (!strcmp(image_type, "mono")) {
+		if (!img_update_realloc(fw->size))
+			memcpy(rbu_data.image_update_buffer,
+				fw->data, fw->size);
+	} else if (!strcmp(image_type, "packet")) {
+		if (!rbu_data.packetsize)
+			rbu_data.packetsize = fw->size;
+		else if (rbu_data.packetsize != fw->size) {
+			packet_empty_list();
+			rbu_data.packetsize = fw->size;
+		}
+		packetize_data(fw->data, fw->size);
+	} else
+		pr_debug("invalid image type specified.\n");
+	spin_unlock(&rbu_data.lock);
+
+	rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+		"dell_rbu", &rbu_device->dev, &context, callbackfn_rbu);
+	if (rc)
+		printk(KERN_ERR
+			"dell_rbu:%s request_firmware_nowait failed"
+			" %d\n", __FUNCTION__, rc);
+}
+
+static int __init
+dcdrbu_init(void)
+{
+	int rc = 0;
+	spin_lock_init(&rbu_data.lock);
+
+	init_packet_head();
+	rbu_device =
+		platform_device_register_simple("dell_rbu", -1, NULL, 0);
+	if (!rbu_device) {
+		printk(KERN_ERR
+			"dell_rbu:%s:platform_device_register_simple "
+			"failed\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	sysfs_create_file(&rbu_device->dev.kobj,
+		&rbu_attr_image_type.attr);
+
+	sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_data_attr);
+
+	rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+		"dell_rbu", &rbu_device->dev, &context, callbackfn_rbu);
+	if (rc)
+		printk(KERN_ERR "dell_rbu:%s:request_firmware_nowait"
+			" failed %d\n", __FUNCTION__, rc);
+
+	return rc;
+
+}
+
+static __exit void
+dcdrbu_exit(void)
+{
+	spin_lock(&rbu_data.lock);
+	packet_empty_list();
+	img_update_free();
+	spin_unlock(&rbu_data.lock);
+	platform_device_unregister(rbu_device);
+}
+
+module_exit(dcdrbu_exit);
+module_init(dcdrbu_init);
diff -uprN linux-2.6.11.11.orig/drivers/firmware/Kconfig linux-2.6.11.11.new/drivers/firmware/Kconfig
--- linux-2.6.11.11.orig/drivers/firmware/Kconfig	2005-06-14 21:00:10.000000000 -0500
+++ linux-2.6.11.11.new/drivers/firmware/Kconfig	2005-07-14 15:43:11.000000000 -0500
@@ -58,4 +58,15 @@ config EFI_PCDP
 
 	  See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf>
 
+config DELL_RBU
+	tristate "BIOS update support for DELL systems via sysfs"
+	default m
+	select FW_LOADER
+	help
+	 Say Y if you want to have the option of updating the BIOS for your
+	 DELL system. Note you need a Dell OpenManage or Dell Update package (DUP) 
+	 supporting application to comunicate with the BIOS regarding the new 
+	 image for the image update to take effect.
+	 See <file:Documentation/dell_rbu.txt> for more details on the driver.
+
 endmenu
diff -uprN linux-2.6.11.11.orig/drivers/firmware/Makefile linux-2.6.11.11.new/drivers/firmware/Makefile
--- linux-2.6.11.11.orig/drivers/firmware/Makefile	2005-06-14 21:01:11.000000000 -0500
+++ linux-2.6.11.11.new/drivers/firmware/Makefile	2005-07-14 14:51:54.000000000 -0500
@@ -4,3 +4,4 @@
 obj-$(CONFIG_EDD)             	+= edd.o
 obj-$(CONFIG_EFI_VARS)		+= efivars.o
 obj-$(CONFIG_EFI_PCDP)		+= pcdp.o
+obj-$(CONFIG_DELL_RBU)          += dell_rbu.o

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [patch 2.6.12-rc3]dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-07-09  1:07 Abhay Salunke
  0 siblings, 0 replies; 28+ messages in thread
From: Abhay Salunke @ 2005-07-09  1:07 UTC (permalink / raw)
  To: linux-kernel, akpm, abhay_salunke; +Cc: greg

>Wrong Subject:  :(
Oops sorry subject line corrected.

>> +struct platform_device *rbu_device;
>> +int context;

>These should not be global variables.
made them static

>You also have some functions that are global, please fix them.
fixed

Signed-off-by: Abhay Salunke <Abhay_Salunke@dell.com>

Thanks
Abhay
diff -uprN linux-2.6.11.11.orig/Documentation/dell_rbu.txt linux-2.6.11.11.new/Documentation/dell_rbu.txt
--- linux-2.6.11.11.orig/Documentation/dell_rbu.txt	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.11.new/Documentation/dell_rbu.txt	2005-06-30 15:41:28.000000000 -0500
@@ -0,0 +1,72 @@
+Purpose:
+Demonstrate the usage of the new open sourced rbu (Remote BIOS Update) driver
+for updating BIOS images on Dell servers and desktops.
+
+Scope:
+This document discusses the functionality of the rbu driver only.
+It does not cover the support needed from aplications to enable the BIOS to
+update itself with the image downloaded in to the memory.
+
+Overview:
+This driver enables userspace applications to update the BIOS on Dell servers
+(starting from servers sold since 1999), desktops and notebooks (starting
+from those sold in 2005).
+
+The driver supports BIOS update using the monilothic image and packetized
+image methods. In case of moniolithic the driver allocates a contiguous chunk
+of physical pages having the BIOS image. In case of packetized the app
+using the driver breaks the image in to packets of fixed sizes and the driver
+would place each packet in contiguous physical memory. The driver also
+maintains a link list of packets for reading them back.
+If the dell_rbu driver is unloaded all the allocated memory is freed.
+
+The rbu driver needs to have an application which will inform the BIOS to
+enable the update in the next system reboot.
+
+The user should not unload the rbu driver after downloading the BIOS image
+or updating.
+
+The driver load creates the following directories under the /sys file system.
+/sys/class/firmware/dell_rbu/loading 
+/sys/class/firmware/dell_rbu/data
+/sys/devices/platform/dell_rbu/image_type
+/sys/devices/platform/dell_rbu/data
+
+The driver supports two types of update mechanism; monolithic and packetized.
+These update mechanism depends upon the BIOS currently running on the system.
+Most of the Dell systems support a monolithic update where the BIOS image is 
+copied to a single contiguous block of physical memory. 
+In case of packet mechanism the single memory can be broken in smaller chuks
+of contiguous memory and the BIOS image is scattered in these packets.
+
+By default the driver uses monolithic memory for the update type. This can be
+changed to contiguous during the driver load time by specifying the load
+parameter image_type=packet.  This can also be changed later as below
+echo "packet" > /sys/devices/platform/dell_rbu/image_type 
+
+Do the steps below to download the BIOS image.
+1) echo 1 > /sys/class/firmware/dell_rbu/loading
+2) cp bios_image.hdr /sys/class/firmware/dell_rbu/data
+3) echo 0 > /sys/class/firmware/dell_rbu/loading
+
+The /sys/class/firmware/dell_rbu/ entries will remain till the following is
+done. 
+echo -1 > /sys/class/firmware/dell_rbu/loading
+
+Until this step is completed the drivr cannot be unloaded.
+
+Also the driver provides /sys/devices/platform/dell_rbu/data readonly file to
+read back the image downloaded. This is useful in case of packet update 
+mechanism where the above steps 1,2,3 will repeated for every packet. 
+By reading the /sys/devices/platform/dell_rbu/data file all packet data 
+downloaded can be verified in a single file. 
+The packets are arranged in this file one after the other in a FIFO order.
+
+NOTE:
+This driver requires a patch for firmware_class.c which has the addition
+of request_firmware_nowait_nohotplug function to wortk
+Also after updating the BIOS image an user mdoe application neeeds to execute
+code which message the BIOS update request to the BIOS. So on the next reboot
+the BIOS knows about the new image downloaded and it updates it self.
+Also don't unload the rbu drive if the image has to be updated.
+
diff -uprN linux-2.6.11.11.orig/drivers/firmware/dell_rbu.c linux-2.6.11.11.new/drivers/firmware/dell_rbu.c
--- linux-2.6.11.11.orig/drivers/firmware/dell_rbu.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.11.new/drivers/firmware/dell_rbu.c	2005-07-08 19:59:41.000000000 -0500
@@ -0,0 +1,627 @@
+/*
+ * dell_rbu.c
+ * Bios Update driver for Dell systems
+ * Author: Dell Inc
+ *         Abhay Salunke <abhay_salunke@dell.com>
+ *
+ * Copyright (C) 2005 Dell Inc.
+ *
+ * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by 
+ * creating entries in the /sys file systems on Linux 2.6 and higher 
+ * kernels. The driver supports two mechanism to update the BIOS namely 
+ * contiguous and packetized. Both these methods still require having some
+ * application to set the CMOS bit indicating the BIOS to update itself 
+ * after a reboot.
+ *
+ * Contiguous method:
+ * This driver writes the incoming data in a monolithic image by allocating 
+ * contiguous physical pages large enough to accommodate the incoming BIOS 
+ * image size.  
+ *
+ * Packetized method:
+ * The driver writes the incoming packet image by allocating a new packet 
+ * on every time the packet data is written. This driver requires an 
+ * application to break the BIOS image in to fixed sized packet chunks.
+ *
+ * See Documentation/dell_rbu.txt for more info.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation
+ *
+ * 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.
+ */
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+
+MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
+MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+#define BIOS_SCAN_LIMIT 0xffffffff
+#define MAX_IMAGE_LENGTH 16
+static struct _rbu_data {
+	void *image_update_buffer;
+	unsigned long image_update_buffer_size;
+	unsigned long bios_image_size;
+	spinlock_t lock;
+	unsigned long packet_read_count;
+	unsigned long packet_write_count;
+	unsigned long num_packets;
+	unsigned long packetsize;
+} rbu_data;
+
+static char image_type[MAX_IMAGE_LENGTH] = "mono";
+module_param_string(image_type, image_type, sizeof (image_type), 0);
+MODULE_PARM_DESC(image_type, "BIOS image type. choose- mono or packet");
+
+struct packet_data {
+	struct list_head list;
+	size_t length;
+	void *data;
+	int ordernum;
+};
+
+static struct packet_data packet_data_head;
+
+static struct platform_device *rbu_device;
+static int context;
+static dma_addr_t dell_rbu_dmaaddr;
+
+static void
+init_packet_head(void)
+{
+	INIT_LIST_HEAD(&packet_data_head.list);
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+static int
+fill_last_packet(void *data, size_t length)
+{
+	struct list_head *ptemp_list;
+	struct packet_data *packet = NULL;
+	int packet_count = 0;
+
+	pr_debug("fill_last_packet: entry \n");
+
+	if (!rbu_data.num_packets) {
+		pr_debug("fill_last_packet: num_packets=0\n");
+		return -ENOMEM;
+	}
+
+	packet_count = rbu_data.num_packets;
+
+	ptemp_list = (&packet_data_head.list)->prev;
+
+	packet = list_entry(ptemp_list, struct packet_data, list);
+
+	if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
+		pr_debug("dell_rbu:%s: packet size data "
+			"overrun\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	pr_debug("fill_last_packet : buffer = %p\n", packet->data);
+
+	memcpy((packet->data + rbu_data.packet_write_count), data, length);
+
+	if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
+		/*
+		 * this was the last data chunk in the packet
+		 * so reinitialize the packet data counter to zero
+		 */
+		rbu_data.packet_write_count = 0;
+	} else
+		rbu_data.packet_write_count += length;
+
+	pr_debug("fill_last_packet: exit \n");
+	return 0;
+}
+
+static int
+create_packet(size_t length)
+{
+	struct packet_data *newpacket;
+	int ordernum = 0;
+
+	pr_debug("create_packet: entry \n");
+
+	if (!rbu_data.packetsize) {
+		pr_debug("create_packet: packetsize not specified\n");
+		return -EINVAL;
+	}
+
+	newpacket = kmalloc(sizeof (struct packet_data), GFP_KERNEL);
+	if (!newpacket) {
+		printk(KERN_WARNING
+			"dell_rbu:%s: failed to allocate new "
+			"packet\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	ordernum = get_order(length);
+	/*
+	 * there is no upper limit on memory 
+	 * address for packetized mechanism 
+	 */
+	newpacket->data = (unsigned char *) __get_free_pages(GFP_KERNEL,
+		ordernum);
+
+	pr_debug("create_packet: newpacket %p\n", newpacket->data);
+
+	if (!newpacket->data) {
+		printk(KERN_WARNING
+			"dell_rbu:%s: failed to allocate new "
+			"packet\n", __FUNCTION__);
+		kfree(newpacket);
+		return -ENOMEM;
+	}
+
+	newpacket->ordernum = ordernum;
+	++rbu_data.num_packets;
+	/*
+	 * initialize the newly created packet headers 
+	 */
+	INIT_LIST_HEAD(&newpacket->list);
+	list_add_tail(&newpacket->list, &packet_data_head.list);
+	/*
+	 * packets have fixed size 
+	 */
+	newpacket->length = rbu_data.packetsize;
+
+	pr_debug("create_packet: exit \n");
+
+	return 0;
+}
+
+static int
+packetize_data(void *data, size_t length)
+{
+	int rc = 0;
+
+	if (!rbu_data.packet_write_count) {
+		if ((rc = create_packet(length)))
+			return rc;
+	}
+	if ((rc = fill_last_packet(data, length)))
+		return rc;
+
+	return rc;
+}
+
+static int
+do_packet_read(char *data, struct list_head *ptemp_list,
+	int length, int bytes_read, int *list_read_count)
+{
+	void *ptemp_buf;
+	struct packet_data *newpacket = NULL;
+	int bytes_copied = 0;
+	int j = 0;
+
+	newpacket = list_entry(ptemp_list, struct packet_data, list);
+	*list_read_count += newpacket->length;
+
+	if (*list_read_count > bytes_read) {
+		/* point to the start of unread data */
+		j = newpacket->length - (*list_read_count - bytes_read);
+		/* point to the offset in the packet buffer */
+		ptemp_buf = (u8 *) newpacket->data + j;
+		/* 
+		 * check if there is enough room in 
+		 * * the incoming buffer 
+		 */
+		if (length > (*list_read_count - bytes_read))
+			/* 
+			 * copy what ever is there in this 
+			 * packet and move on 
+			 */
+			bytes_copied = (*list_read_count - bytes_read);
+		else
+			/* copy the remaining */
+			bytes_copied = length;
+		memcpy(data, ptemp_buf, bytes_copied);
+	}
+	return bytes_copied;
+}
+
+static int
+packet_read_list(char *data, size_t * pread_length)
+{
+	struct list_head *ptemp_list;
+	int temp_count = 0;
+	int bytes_copied = 0;
+	int bytes_read = 0;
+	int remaining_bytes = 0;
+	char *pdest = data;
+
+	/* check if we have any packets */
+	if (0 == rbu_data.num_packets)
+		return -ENOMEM;
+
+	remaining_bytes = *pread_length;
+	bytes_read = rbu_data.packet_read_count;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while (!list_empty(ptemp_list)) {
+		bytes_copied = do_packet_read(pdest, ptemp_list,
+			remaining_bytes, bytes_read, &temp_count);
+		remaining_bytes -= bytes_copied;
+		bytes_read += bytes_copied;
+		pdest += bytes_copied;
+		/*
+		 * check if we reached end of buffer before reaching the
+		 * last packet
+		 */
+		if (remaining_bytes == 0)
+			break;
+
+		ptemp_list = ptemp_list->next;
+	}
+	/*finally set the bytes read */
+	*pread_length = bytes_read - rbu_data.packet_read_count;
+	rbu_data.packet_read_count = bytes_read;
+	return 0;
+}
+
+static void
+packet_empty_list(void)
+{
+	struct list_head *ptemp_list;
+	struct list_head *pnext_list;
+	struct packet_data *newpacket;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while (!list_empty(ptemp_list)) {
+		newpacket =
+			list_entry(ptemp_list, struct packet_data, list);
+		pnext_list = ptemp_list->next;
+		list_del(ptemp_list);
+		ptemp_list = pnext_list;
+		/*
+		 * zero out the RBU packet memory before freeing 
+		 * to make sure there are no stale RBU packets left in memory
+		 */
+		memset(newpacket->data, 0, rbu_data.packetsize);
+		free_pages((unsigned long) newpacket->data,
+			newpacket->ordernum);
+		kfree(newpacket);
+	}
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+/*
+ * img_update_free: Frees the buffer allocated for storing BIOS image
+ * Always called with lock held and returned with lock held 
+ */
+static void
+img_update_free(void)
+{
+	if (!rbu_data.image_update_buffer)
+		return;
+	/*
+	 * zero out this buffer before freeing it to get rid of any stale
+	 * BIOS image copied in memory.
+	 */
+	memset(rbu_data.image_update_buffer, 0,
+		rbu_data.image_update_buffer_size);
+	dma_free_coherent(NULL, rbu_data.bios_image_size,
+		rbu_data.image_update_buffer, dell_rbu_dmaaddr);
+
+	/*
+	 * Re-initialize the rbu_data variables after a free 
+	 */
+	rbu_data.image_update_buffer = NULL;
+	rbu_data.image_update_buffer_size = 0;
+	rbu_data.bios_image_size = 0;
+}
+
+/*
+ * img_update_realloc: This function allocates the contiguous pages to
+ * accommodate the requested size of data. The memory address and size
+ * values are stored globally and on every call to this function the new
+ * size is checked to see if more data is required than the existing size. 
+ * If true the previous memory is freed and new allocation is done to
+ * accommodate the new size. If the incoming size is less then than the
+ * already allocated size, then that memory is reused. This function is
+ * called with lock held and returns with lock held. 
+ */
+static int
+img_update_realloc(unsigned long size)
+{
+	unsigned char *image_update_buffer = NULL;
+	unsigned long rc;
+
+	/*
+	 * check if the buffer of sufficient size has been 
+	 * already allocated 
+	 */
+	if (rbu_data.image_update_buffer_size >= size) {
+		/*
+		 * check for corruption 
+		 */
+		if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {
+			printk(KERN_ERR "dell_rbu:%s: corruption "
+				"check failed\n", __FUNCTION__);
+			return -EINVAL;
+		}
+		/*
+		 * we have a valid pre-allocated buffer with 
+		 * sufficient size 
+		 */
+		return 0;
+	}
+
+	/*
+	 * free any previously allocated buffer 
+	 */
+	img_update_free();
+
+	spin_unlock(&rbu_data.lock);
+
+	image_update_buffer = dma_alloc_coherent(NULL, size,
+		&dell_rbu_dmaaddr, GFP_KERNEL);
+
+	spin_lock(&rbu_data.lock);
+
+	if (image_update_buffer != NULL) {
+		rbu_data.image_update_buffer = image_update_buffer;
+		rbu_data.image_update_buffer_size = size;
+		rbu_data.bios_image_size =
+			rbu_data.image_update_buffer_size;
+		rc = 0;
+	} else {
+		pr_debug("Not enough memory for image update:"
+			"size = %ld\n", size);
+		rc = -ENOMEM;
+	}
+
+	return rc;
+}
+
+static ssize_t
+read_packet_data(char *buffer, loff_t pos, size_t count)
+{
+	int retval;
+	size_t bytes_left;
+	size_t data_length;
+	char *ptempBuf = buffer;
+	unsigned long imagesize;
+
+	/* check to see if we have something to return */
+	if (rbu_data.num_packets == 0) {
+		pr_debug("read_packet_data: no packets written\n");
+		retval = -ENOMEM;
+		goto read_rbu_data_exit;
+	}
+
+	imagesize = rbu_data.num_packets * rbu_data.packetsize;
+
+	if (pos > imagesize) {
+		retval = 0;
+		printk(KERN_WARNING "dell_rbu:read_packet_data: "
+			"data underrun\n");
+		goto read_rbu_data_exit;
+	}
+
+	bytes_left = imagesize - pos;
+	data_length = min(bytes_left, count);
+
+	if ((retval = packet_read_list(ptempBuf, &data_length)) < 0)
+		goto read_rbu_data_exit;
+
+	if ((pos + count) > imagesize) {
+		rbu_data.packet_read_count = 0;
+		/* this was the last copy */
+		retval = bytes_left;
+	} else
+		retval = count;
+
+      read_rbu_data_exit:
+	return retval;
+}
+
+static ssize_t
+read_rbu_mono_data(char *buffer, loff_t pos, size_t count)
+{
+	unsigned char *ptemp = NULL;
+	size_t bytes_left = 0;
+	size_t data_length = 0;
+	ssize_t ret_count = 0;
+
+	/* check to see if we have something to return */
+	if ((rbu_data.image_update_buffer == NULL) ||
+		(rbu_data.bios_image_size == 0)) {
+		pr_debug("read_rbu_data_mono: image_update_buffer %p ,"
+			"bios_image_size %lu\n",
+			rbu_data.image_update_buffer,
+			rbu_data.bios_image_size);
+		ret_count = -ENOMEM;
+		goto read_rbu_data_exit;
+	}
+
+	if (pos > rbu_data.bios_image_size) {
+		ret_count = 0;
+		goto read_rbu_data_exit;
+	}
+
+	bytes_left = rbu_data.bios_image_size - pos;
+	data_length = min(bytes_left, count);
+
+	ptemp = rbu_data.image_update_buffer;
+	memcpy(buffer, (ptemp + pos), data_length);
+
+	if ((pos + count) > rbu_data.bios_image_size)
+		/* this was the last copy */
+		ret_count = bytes_left;
+	else
+		ret_count = count;
+read_rbu_data_exit:
+	return ret_count;
+}
+
+static ssize_t
+read_rbu_data(struct kobject *kobj, char *buffer, loff_t pos, size_t count)
+{
+	ssize_t ret_count = 0;
+
+	spin_lock(&rbu_data.lock);
+
+	if (!strcmp(image_type, "mono"))
+		ret_count = read_rbu_mono_data(buffer, pos, count);
+	else if (!strcmp(image_type, "packet"))
+		ret_count = read_packet_data(buffer, pos, count);
+	else
+		pr_debug("read_rbu_data: invalid image type specified\n");
+
+	spin_unlock(&rbu_data.lock);
+	return ret_count;
+}
+
+static ssize_t
+rbu_show_image_type(struct platform_device *rbu_dev, char *buf)
+{
+	unsigned int size = 0;
+	size = sprintf(buf, "%s\n", image_type);
+	return size;
+}
+
+static ssize_t
+rbu_store_image_type(struct platform_device *rbu_dev,
+	const char *buf, size_t count)
+{
+	int rc = count;
+	spin_lock(&rbu_data.lock);
+
+	if (strlen(buf) < MAX_IMAGE_LENGTH)
+		sscanf(buf, "%s", image_type);
+	else
+		printk(KERN_WARNING "dell_rbu: image_type is invalid"
+			"max chars = %d\n", MAX_IMAGE_LENGTH);
+
+	/* we must free all previous allocations */
+	packet_empty_list();
+	img_update_free();
+
+	spin_unlock(&rbu_data.lock);
+	return rc;
+}
+
+struct rbu_attribute {
+	struct attribute attr;
+	 ssize_t(*show) (struct platform_device * rbu_dev, char *buf);
+	 ssize_t(*store) (struct platform_device * rbu_dev,
+		const char *buf, size_t count);
+};
+
+#define RBU_DEVICE_ATTR(_name,_mode,_show,_store ) \
+struct rbu_attribute rbu_attr_##_name = {       \
+	.attr ={.name= __stringify(_name), .mode= _mode, .owner= THIS_MODULE},\
+	.show = _show,                                \
+	.store = _store,                                \
+};
+
+static RBU_DEVICE_ATTR(image_type, 0644, rbu_show_image_type,
+	rbu_store_image_type);
+
+static struct bin_attribute rbu_data_attr = {
+	.attr = {.name = "data",.owner = THIS_MODULE,.mode = 0444},
+	.read = read_rbu_data,
+};
+
+static void
+callbackfn_rbu(const struct firmware *fw, void *context)
+{
+	int rc = 0;
+
+	if (!fw || !fw->size)
+		return;
+
+	spin_lock(&rbu_data.lock);
+	if (!strcmp(image_type, "mono")) {
+		if (!img_update_realloc(fw->size))
+			memcpy(rbu_data.image_update_buffer,
+				fw->data, fw->size);
+	} else if (!strcmp(image_type, "packet")) {
+		if (!rbu_data.packetsize)
+			rbu_data.packetsize = fw->size;
+		else if (rbu_data.packetsize != fw->size) {
+			packet_empty_list();
+			rbu_data.packetsize = fw->size;
+		}
+		packetize_data(fw->data, fw->size);
+	} else
+		pr_debug("invalid image type specified.\n");
+	spin_unlock(&rbu_data.lock);
+
+	rc = request_firmware_nowait_nohotplug(THIS_MODULE,
+		"dell_rbu", &rbu_device->dev, &context, callbackfn_rbu);
+	if (rc)
+		printk(KERN_ERR
+			"dell_rbu:%s request_firmware_nowait failed"
+			" %d\n", __FUNCTION__, rc);
+}
+
+static int __init
+dcdrbu_init(void)
+{
+	int rc = 0;
+	spin_lock_init(&rbu_data.lock);
+
+	init_packet_head();
+
+	rbu_device =
+		platform_device_register_simple("dell_rbu", -1, NULL, 0);
+	if (!rbu_device) {
+		printk(KERN_ERR
+			"dell_rbu:%s:platform_device_register_simple "
+			"failed\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	sysfs_create_file(&rbu_device->dev.kobj,
+		&rbu_attr_image_type.attr);
+
+	sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_data_attr);
+
+	rc = request_firmware_nowait_nohotplug(THIS_MODULE,
+		"dell_rbu", &rbu_device->dev, &context, callbackfn_rbu);
+	if (rc)
+		printk(KERN_ERR "dell_rbu:%s:request_firmware_nowait"
+			" failed %d\n", __FUNCTION__, rc);
+
+	return rc;
+
+}
+
+static __exit void
+dcdrbu_exit(void)
+{
+	spin_lock(&rbu_data.lock);
+	packet_empty_list();
+	img_update_free();
+	spin_unlock(&rbu_data.lock);
+	platform_device_unregister(rbu_device);
+}
+
+module_exit(dcdrbu_exit);
+module_init(dcdrbu_init);
diff -uprN linux-2.6.11.11.orig/drivers/firmware/Kconfig linux-2.6.11.11.new/drivers/firmware/Kconfig
--- linux-2.6.11.11.orig/drivers/firmware/Kconfig	2005-06-14 21:00:10.000000000 -0500
+++ linux-2.6.11.11.new/drivers/firmware/Kconfig	2005-06-30 15:37:43.000000000 -0500
@@ -58,4 +58,15 @@ config EFI_PCDP
 
 	  See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf>
 
+config DELL_RBU
+	tristate "BIOS update support for DELL systems via sysfs"
+	default y
+	select FW_LOADER
+	help
+	 Say Y if you want to have the option of updating the BIOS for your
+	 DELL system. Note you need a supporting application to comunicate
+	 with the BIOS regardin the new image for the image update to
+	 take effect.
+	 See <file:Documentation/dell_rbu.txt> for more details on the driver.
+
 endmenu
diff -uprN linux-2.6.11.11.orig/drivers/firmware/Makefile linux-2.6.11.11.new/drivers/firmware/Makefile
--- linux-2.6.11.11.orig/drivers/firmware/Makefile	2005-06-14 21:01:11.000000000 -0500
+++ linux-2.6.11.11.new/drivers/firmware/Makefile	2005-06-30 15:37:35.000000000 -0500
@@ -4,3 +4,4 @@
 obj-$(CONFIG_EDD)             	+= edd.o
 obj-$(CONFIG_EFI_VARS)		+= efivars.o
 obj-$(CONFIG_EFI_PCDP)		+= pcdp.o
+obj-$(CONFIG_DELL_RBU)          += dell_rbu.o

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-06-15 17:59 Abhay Salunke
  2005-06-16 18:52 ` Greg KH
@ 2005-06-20  0:36 ` Andrew Morton
  1 sibling, 0 replies; 28+ messages in thread
From: Andrew Morton @ 2005-06-20  0:36 UTC (permalink / raw)
  To: Abhay Salunke
  Cc: linux-kernel, dmitry.torokhov, abhay_salunke, matt_domsch, greg


This patch causes my x86_64 box to oops during udev startup:


Starting udev:  Unable to handle kernel NULL pointer dereference at 0000000000000040 RIP: 
<ffffffff801a807c>{sysfs_readdir+350}                                                     
PGD 17be07067 PUD 17add5067 PMD 0    
Oops: 0000 [1] PREEMPT SMP        
CPU 2                      
Modules linked in:
Pid: 27686, comm: udevstart Not tainted 2.6.12-mm1
RIP: 0010:[<ffffffff801a807c>] <ffffffff801a807c>{sysfs_readdir+350}
RSP: 0018:ffff81017adc5ea8  EFLAGS: 00010286                        
RAX: 0000000000000000 RBX: ffff81017ceb18d0 RCX: 0000000000000007
RDX: ffffffff80184516 RSI: ffff81017adc5f38 RDI: ffff81017b45d41c
RBP: ffff81017aa5cd50 R08: 0000000000015bc0 R09: 000000302062d6b8
R10: 000000302062d6b8 R11: 0000000000000246 R12: ffff81017ceb18c8
R13: ffff81017fc663a0 R14: ffff81017edfaa70 R15: ffff81017b45d414
FS:  00002aaaaaadcde0(0000) GS:ffffffff805a0780(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b                           
CR2: 0000000000000040 CR3: 000000017f2d5000 CR4: 00000000000006e0
Process udevstart (pid: 27686, threadinfo ffff81017adc4000, task ffff81017cb77030)
Stack: 0000000700000246 ffffffff80184516 ffff81017adc5f38 ffff81017edfaa70        
       ffff81017fc67860 00000000fffffffe ffff81017fc67928 ffffffff80184516 
       ffff81017adc5f38 ffffffff8018427a                                   
Call Trace:<ffffffff80184516>{filldir64+0} <ffffffff80184516>{filldir64+0}
       <ffffffff8018427a>{vfs_readdir+126} <ffffffff80184643>{sys_getdents64+116}
       <ffffffff80171969>{sys_close+132} <ffffffff8010d9e2>{system_call+126}     
                                                                            
       
Code: 48 8b 40 40 eb 11 48 8b 3d 4f eb 3d 00 be 02 00 00 00 e8 86 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-06-17 14:55 Abhay_Salunke
@ 2005-06-17 15:29 ` Greg KH
  0 siblings, 0 replies; 28+ messages in thread
From: Greg KH @ 2005-06-17 15:29 UTC (permalink / raw)
  To: Abhay_Salunke; +Cc: linux-kernel, akpm, dmitry.torokhov, Matt_Domsch

On Fri, Jun 17, 2005 at 09:55:31AM -0500, Abhay_Salunke@Dell.com wrote:
> > On Wed, Jun 15, 2005 at 12:59:46PM -0500, Abhay Salunke wrote:
> > > +static struct device rbu_device_mono;
> > > +static struct device rbu_device_packet;
> > > +static struct device rbu_device_cancel;
> > 
> > You should never create a struct device on the stack.  Lots of bad
> > things can happen (including not having a release function for them.)
> > 
> they are not declared inside any function; can they be on stack?

Sorry, I didn't mean "on the stack" I ment, they are static and not
dynamically allocated.

> > Why not just point to the cpu device, or some other platform or system
> > device?
> > 
> Not sure what these devices are for and didn't want to mess with them.

Ok, then I suggest you look into them then :)

Again, creating a struct device that is not dynamically allocated is not
allowed.  And creating a struct device that is not tied into the driver
tree, is also a bad thing.  Use the ones that are already present, or
register yours with the core so they show up properly.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-06-17 14:55 Abhay_Salunke
  2005-06-17 15:29 ` Greg KH
  0 siblings, 1 reply; 28+ messages in thread
From: Abhay_Salunke @ 2005-06-17 14:55 UTC (permalink / raw)
  To: greg; +Cc: linux-kernel, akpm, dmitry.torokhov, Matt_Domsch



> -----Original Message-----
> From: Greg KH [mailto:greg@kroah.com]
> Sent: Thursday, June 16, 2005 1:52 PM
> To: Salunke, Abhay
> Cc: linux-kernel@vger.kernel.org; Andrew Morton; Dmitry Torokhov;
Domsch,
> Matt
> Subject: Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new
Dell
> BIOS update driver
> 
> On Wed, Jun 15, 2005 at 12:59:46PM -0500, Abhay Salunke wrote:
> > +static struct device rbu_device_mono;
> > +static struct device rbu_device_packet;
> > +static struct device rbu_device_cancel;
> 
> You should never create a struct device on the stack.  Lots of bad
> things can happen (including not having a release function for them.)
> 
they are not declared inside any function; can they be on stack?
> Why not just point to the cpu device, or some other platform or system
> device?
> 
Not sure what these devices are for and didn't want to mess with them.> 

Thanks
Abhay

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-06-15 17:59 Abhay Salunke
@ 2005-06-16 18:52 ` Greg KH
  2005-06-20  0:36 ` Andrew Morton
  1 sibling, 0 replies; 28+ messages in thread
From: Greg KH @ 2005-06-16 18:52 UTC (permalink / raw)
  To: Abhay Salunke; +Cc: linux-kernel, Andrew Morton, Dmitry Torokhov, matt_domsch

On Wed, Jun 15, 2005 at 12:59:46PM -0500, Abhay Salunke wrote:
> +static struct device rbu_device_mono;
> +static struct device rbu_device_packet;
> +static struct device rbu_device_cancel;

You should never create a struct device on the stack.  Lots of bad
things can happen (including not having a release function for them.)

Why not just point to the cpu device, or some other platform or system
device?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-06-15 17:59 Abhay Salunke
  2005-06-16 18:52 ` Greg KH
  2005-06-20  0:36 ` Andrew Morton
  0 siblings, 2 replies; 28+ messages in thread
From: Abhay Salunke @ 2005-06-15 17:59 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton, Dmitry Torokhov
  Cc: abhay_salunke, matt_domsch, Greg KH

This patch uses request_firmware_nowait without having to modify any firmware_class.c code.

By making a contribution to this project, I certify that:
The contribution was created in whole or in part by me and I have the
right to submit it under the open source license indicated in the file.
Resubmitting after cleaning up spaces/tabs etc...

Signed-off-by: Abhay Salunke <Abhay_Salunke@dell.com>

Thanks,
Abhay Salunke
Software Engineer.
DELL Inc

diff -uprN /usr/src/linux-2.6.11.11.orig/Documentation/dell_rbu.txt /usr/src/linux-2.6.11.11/Documentation/dell_rbu.txt
--- /usr/src/linux-2.6.11.11.orig/Documentation/dell_rbu.txt	1969-12-31 18:00:00.000000000 -0600
+++ /usr/src/linux-2.6.11.11/Documentation/dell_rbu.txt	2005-06-14 21:02:09.000000000 -0500
@@ -0,0 +1,62 @@
+Purpose:
+Demonstrate the usage of the new open sourced rbu (Remote BIOS Update) driver 
+for updating BIOS images on Dell hardware.
+
+Scope:
+This document discusses the functionality of the rbu driver only. 
+It does not cover the mechanism related to flipping of the CMOS bit to tell 
+the BIOS for reflashing it self.
+
+Overview:
+The rbu driver is designed to be running on 2.6 kernel. 
+This driver is a open sourced code with one rbu.c (total lines ~500) file.
+The driver supports BIOS update using the monilothic image and packetized
+image methods. In case of moniolithic the driver allocates a contiguous chunk
+of physical pages having the BIOS image. In case of packetized the app
+using the driver breaks the image in to packets of fixed sizes and the driver
+woudl place each packet in contiguous physical memory. The driver also
+maintains a link list of packets if for reading them back.
+If the dell_rbu driver is unloaded all the allocated memory is freed.
+
+The rbu driver needs to have an aplication which flips the CMOS bit to enable 
+the BIOS update after a reboot.
+
+The user should not unload the rbu driver after downloading the BIOS image for updating.
+
+The driver load creates the following directories under the /sys file system.
+/sys/class/firmware/dell_rbu_mono
+/sys/class/firmware/dell_rbu_packet
+/sys/class/firmware/dell_rbu_cancel
+
+each of these directories have the files loading and data.
+
+Always before starting the BIOS update do 
+1> capture value of /sys/class/firmware/timeout 
+2> echo 0 > /sys/class/firmware/timeout
+before exiting the BIOS update script restore the timeout value.
+
+Steps for updating a monolithic image
+1> echo 1 > /sys/class/firmware/dell_rbu_mono/loading
+2> cp image_file /sys/class/firmware/dell_rbu_mono/data
+3> echo 0 > /sys/class/firmware/dell_rbu_mono/loading
+
+Steps for updating a packet image
+1> echo 1 > /sys/class/firmware/dell_rbu_packet/loading
+2> cp image_file /sys/class/firmware/dell_rbu_packet/data
+3> echo 0 > /sys/class/firmware/dell_rbu_packet/loading
+
+The uploaded image can be freed without unloading the driver as follows.
+echo "-1" > /sys/class/firmware/dell_rbu_cancel/loading
+
+
+The user can overwrite the data file with a new image. 
+The user has to make sure that the new image size is less than or equal to the 
+image size copied to the rbudatasize file. 
+If the new image is grater than the allocated size then only the allocated size gets copied the rest will not.
+
+NOTE:
+Afte updating the BIOS image the CMOS bit has to be set by some application to enable the update.
+Also dont unload the rbu drive if the image has to be updated.
+
+
+
diff -uprN /usr/src/linux-2.6.11.11.orig/drivers/firmware/dell_rbu.c /usr/src/linux-2.6.11.11/drivers/firmware/dell_rbu.c
--- /usr/src/linux-2.6.11.11.orig/drivers/firmware/dell_rbu.c	1969-12-31 18:00:00.000000000 -0600
+++ /usr/src/linux-2.6.11.11/drivers/firmware/dell_rbu.c	2005-06-15 15:05:13.928810384 -0500
@@ -0,0 +1,487 @@
+/*
+ * dell_rbu.c
+ * Bios Update driver for Dell systems
+ * Author: Dell Inc
+ *	   Abhay Salunke <abhay_salunke@dell.com>
+ *
+ * Copyright (C) 2005 Dell Inc.
+ *
+ * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by 
+ * creating entries in the /sys file systems on Linux 2.6 and higher 
+ * kernels. The driver supports two mechanism to update the BIOS namely 
+ * contiguous and packetized. Both these methods still require having some
+ * application to set the CMOS bit indicating the BIOS to update itself 
+ * after a reboot.
+ *
+ * Contiguous method:
+ * This driver writes the incoming data in a monolithic image by allocating 
+ * contiguous physical pages large enough to accommodate the incoming BIOS 
+ * image size.  
+ *
+ * Packetized method:
+ * The driver writes the incoming packet image by allocating a new packet 
+ * on every time the packet data is written. This driver requires an 
+ * application to break the BIOS image in to fixed sized packet chunks.
+ *
+ * See Documentation/dell_rbu.txt for more info.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation
+ *
+ * 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.
+ */
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+
+MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
+MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+#define BIOS_SCAN_LIMIT 0xffffffff
+
+static struct _rbu_data {
+	void *image_update_buffer;
+	unsigned long image_update_buffer_size;
+	unsigned long bios_image_size;
+	unsigned long image_update_order_number;
+	spinlock_t lock;
+	unsigned long packet_read_count;
+	unsigned long packet_write_count;
+	unsigned long num_packets;
+	unsigned long packetsize;
+} rbu_data;
+
+struct packet_data {
+	struct list_head list;
+	size_t length;
+	void *data;
+	int ordernum;
+};
+
+static struct packet_data packet_data_head;
+
+static void init_packet_head(void)
+{
+	INIT_LIST_HEAD(&packet_data_head.list);
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+static struct device rbu_device_mono;
+static struct device rbu_device_packet;
+static struct device rbu_device_cancel;
+
+static int fill_last_packet(void *data, size_t length)
+{
+	struct list_head *ptemp_list;
+	struct packet_data *ppacket = NULL;
+	int packet_count = 0;
+
+	pr_debug("fill_last_packet: entry \n");
+
+	/* check if we have any packets */
+	if (0 == rbu_data.num_packets) {
+		pr_debug("fill_last_packet: num_packets=0\n");
+		return -ENOMEM;
+	}
+
+	packet_count = rbu_data.num_packets;
+
+	ptemp_list = (&packet_data_head.list)->next;
+
+	while (--packet_count)
+		ptemp_list = ptemp_list->next;
+
+	ppacket = list_entry(ptemp_list, struct packet_data, list);
+
+	if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
+		printk(KERN_WARNING "fill_last_packet: packet size data "
+		       "overrun\n");
+		return -ENOMEM;
+	}
+
+	pr_debug("fill_last_packet : buffer = %p\n", ppacket->data);
+
+	/* copy the incoming data in to the new buffer */
+	memcpy((ppacket->data + rbu_data.packet_write_count), data, length);
+
+	if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
+		/*
+		   this was the last data chunk in the packet
+		   so reinitialize the packet data counter to zero
+		 */
+		rbu_data.packet_write_count = 0;
+	} else
+		rbu_data.packet_write_count += length;
+
+	pr_debug("fill_last_packet: exit \n");
+	return 0;
+}
+
+/*
+ get_free_pages_limited:
+ This is a helper function which allocates free pages based on an upper limit.
+ On x86_64 or 64 bit arch the memory allocation goes above 4GB space which is
+ not addressable by the BIOS. This function tries to get allocation below the
+ limit (4GB) address. It first tries to allocate memory normally using the
+ GFP_KERNEL argument if the incoming limit is non-zero and if the returned
+ physical memory address exceeds the upper limit, the allocated pages are 
+ freed and the memory is reallocated using the GFP_DMA argument.
+*/
+static void *get_free_pages_limited(unsigned long size, int *ordernum,
+				    unsigned long limit)
+{
+	unsigned long img_buf_phys_addr;
+	void *pbuf = NULL;
+
+	*ordernum = get_order(size);
+	/*
+	   Check if we are not getting a very large file.
+	   This can happen as a user error in entering the file size
+	 */
+	if (*ordernum == BITS_PER_LONG) {
+		pr_debug("get_free_pages_limited: Incoming size is"
+			 " very large\n");
+		return NULL;
+	}
+
+	/* try allocating a new buffer to fit the request */
+	pbuf = (unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);
+
+	if (pbuf) {
+		/* check if the image is with in limits */
+		img_buf_phys_addr = (unsigned long)virt_to_phys(pbuf);
+
+		if (!limit && ((img_buf_phys_addr + size) > limit)) {
+			pr_debug("Got memory above 4GB range, free this "
+				 "and try with DMA memory\n");
+			/*
+			   free this memory as we need it with in 
+			   4GB range 
+			 */
+			free_pages((unsigned long)pbuf, *ordernum);
+			/*
+			   Try allocating a new buffer from the 
+			   GFP_DMA range as it is with in 16MB range.
+			 */
+			pbuf = (unsigned char *)__get_free_pages(GFP_DMA,
+								 *ordernum);
+			if (!pbuf)
+				pr_debug("Failed to get memory "
+					 "of size %ld "
+					 "using GFP_DMA\n", size);
+		}
+	}
+	return pbuf;
+}
+
+static int create_packet(size_t length)
+{
+	struct packet_data *newpacket;
+	int ordernum = 0;
+
+	pr_debug("create_packet: entry \n");
+
+	if (!rbu_data.packetsize) {
+		pr_debug("create_packet: packetsize not specified\n");
+		return -EINVAL;
+	}
+
+	newpacket = kmalloc(sizeof(struct packet_data), GFP_KERNEL);
+	if (!newpacket) {
+		printk(KERN_WARNING "create_packet: failed to allocate new "
+		       "packet\n");
+		return -ENOMEM;
+	}
+
+	/* there is no upper limit on memory address for packetized mechanism */
+	newpacket->data = get_free_pages_limited(rbu_data.packetsize,
+						 &ordernum, 0);
+	pr_debug("create_packet: newpacket %p\n", newpacket->data);
+
+	if (!newpacket->data) {
+		printk(KERN_WARNING "create_packet: failed to allocate new "
+		       "packet\n");
+		kfree(newpacket);
+		return -ENOMEM;
+	}
+
+	newpacket->ordernum = ordernum;
+	++rbu_data.num_packets;
+	/* initialize the newly created packet headers */
+	INIT_LIST_HEAD(&newpacket->list);
+	list_add_tail(&newpacket->list, &packet_data_head.list);
+	/* packets have fixed size */
+	newpacket->length = rbu_data.packetsize;
+
+	pr_debug("create_packet: exit \n");
+
+	return 0;
+}
+
+static int packetize_data(void *data, size_t length)
+{
+	int rc = 0;
+
+	if (!rbu_data.packet_write_count) {
+		if ((rc = create_packet(length)))
+			return rc;
+	}
+	/* fill data in to the packet */
+	if ((rc = fill_last_packet(data, length)))
+		return rc;
+
+	return rc;
+}
+
+static void packet_empty_list(void)
+{
+	struct list_head *ptemp_list;
+	struct list_head *pnext_list;
+	struct packet_data *newpacket;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while (!list_empty(ptemp_list)) {
+		newpacket = list_entry(ptemp_list, struct packet_data, list);
+		pnext_list = ptemp_list->next;
+		list_del(ptemp_list);
+		ptemp_list = pnext_list;
+		/*
+		   zero out the RBU packet memory before freeing 
+		   to make sure there are no stale RBU packets left in memory
+		 */
+		memset(newpacket->data, 0, rbu_data.packetsize);
+		free_pages((unsigned long)newpacket->data, newpacket->ordernum);
+		kfree(newpacket);
+	}
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+/*
+ img_update_free:
+ Frees the buffer allocated for storing BIOS image
+ Always called with lock held and returned with lock held
+*/
+static void img_update_free(void)
+{
+	if (!rbu_data.image_update_buffer)
+		return;
+	/*
+	   zero out this buffer before freeing it to get rid of any stale
+	   BIOS image copied in memory.
+	 */
+	memset(rbu_data.image_update_buffer, 0,
+	       rbu_data.image_update_buffer_size);
+	free_pages((unsigned long)rbu_data.image_update_buffer,
+		   rbu_data.image_update_order_number);
+	/* Re-initialize the rbu_data variables after a free */
+	rbu_data.image_update_buffer = NULL;
+	rbu_data.image_update_buffer_size = 0;
+	rbu_data.bios_image_size = 0;
+}
+
+/*
+ img_update_realloc:
+ This function allocates the contiguous pages to accommodate the requested
+ size of data. The memory address and size values are stored globally and
+ on every call to this function the new size is checked to see if more
+ data is required than the existing size. If true the previous memory is
+ freed and new allocation is done to accommodate the new size. If the 
+ incoming size is less then than the already allocated size, then that
+ memory is reused.
+ This function is called with lock held and returns with lock held.
+*/
+static int img_update_realloc(unsigned long size)
+{
+	unsigned char *image_update_buffer = NULL;
+	unsigned long rc;
+	int ordernum = 0;
+
+	/* 
+	   check if the buffer of sufficient size has been 
+	   already allocated 
+	 */
+	if (rbu_data.image_update_buffer_size >= size) {
+		/* check for corruption */
+		if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {
+			printk(KERN_ERR "img_update_realloc: "
+			       "corruption check failed\n");
+			return -EINVAL;
+		}
+		/* 
+		   we have a valid pre-allocated buffer with 
+		   sufficient size 
+		 */
+		return 0;
+	}
+
+	/* free any previously allocated buffer */
+	img_update_free();
+
+	/*
+	   This has already been called as locked so we can now unlock
+	   and proceed to calling get_free_pages_limited as this function
+	   can sleep
+	 */
+	spin_unlock(&rbu_data.lock);
+
+	image_update_buffer = (unsigned char *)get_free_pages_limited(size,
+								      &ordernum,
+								      BIOS_SCAN_LIMIT);
+
+	/* acquire the spinlock again */
+	spin_lock(&rbu_data.lock);
+
+	if (image_update_buffer != NULL) {
+		rbu_data.image_update_buffer = image_update_buffer;
+		rbu_data.image_update_buffer_size = PAGE_SIZE << ordernum;
+		rbu_data.image_update_order_number = ordernum;
+		memset(rbu_data.image_update_buffer, 0,
+		       rbu_data.image_update_buffer_size);
+		rc = 0;
+	} else {
+		pr_debug("Not enough memory for image update:order"
+			 " number = %d,size = %ld\n", ordernum, size);
+		rc = -ENOMEM;
+	}
+
+	return rc;
+}				/* img_update_realloc */
+
+int context;
+
+void callbackfn_mono(const struct firmware *fw, void *context);
+void callbackfn_packet(const struct firmware *fw, void *context);
+void callbackfn_cancel(const struct firmware *fw, void *context);
+
+void callbackfn_cancel(const struct firmware *fw, void *context)
+{
+	int rc;
+	if (!fw) {
+		spin_lock(&rbu_data.lock);
+		packet_empty_list();
+		img_update_free();
+		spin_unlock(&rbu_data.lock);
+	}
+	if ((rc = request_firmware_nowait(THIS_MODULE,
+					  "dell_rbu_cancel", &rbu_device_cancel,
+					  &context, callbackfn_mono)))
+		printk(KERN_ERR "%s rbu_cancel request_firmware_nowait"
+		       " failed %d\n", __FUNCTION__, rc);
+
+}
+
+void callbackfn_packet(const struct firmware *fw, void *context)
+{
+	int rc;
+	if (fw) {
+		spin_lock(&rbu_data.lock);
+		if (!rbu_data.packetsize)
+			rbu_data.packetsize = fw->size;
+		else if (rbu_data.packetsize != fw->size) {
+			packet_empty_list();
+			rbu_data.packetsize = fw->size;
+		}
+		packetize_data(fw->data, fw->size);
+		spin_unlock(&rbu_data.lock);
+	}
+	if ((rc = request_firmware_nowait(THIS_MODULE,
+					  "dell_rbu_packet", &rbu_device_packet,
+					  &context, callbackfn_packet)))
+		printk(KERN_ERR "%s rbu_packet request_firmware_nowait"
+		       " failed %d\n", __FUNCTION__, rc);
+
+}
+
+void callbackfn_mono(const struct firmware *fw, void *context)
+{
+	int rc;
+	if (fw) {
+		spin_lock(&rbu_data.lock);
+		if (!img_update_realloc(fw->size))
+			memcpy(rbu_data.image_update_buffer,
+			       fw->data, fw->size);
+		spin_unlock(&rbu_data.lock);
+	}
+	if ((rc = request_firmware_nowait(THIS_MODULE,
+					  "dell_rbu_mono_", &rbu_device_mono,
+					  &context, callbackfn_mono)))
+		printk(KERN_ERR "%s rbu_mono request_firmware_nowait"
+		       " failed %d\n", __FUNCTION__, rc);
+
+}
+
+static int __init dcdrbu_init(void)
+{
+	int rc = 0;
+	spin_lock_init(&rbu_data.lock);
+
+	init_packet_head();
+
+	device_initialize(&rbu_device_mono);
+	device_initialize(&rbu_device_packet);
+	device_initialize(&rbu_device_cancel);
+
+	strncpy(rbu_device_mono.bus_id, "dell_rbu_mono", BUS_ID_SIZE);
+	strncpy(rbu_device_packet.bus_id, "dell_rbu_packet", BUS_ID_SIZE);
+	strncpy(rbu_device_cancel.bus_id, "dell_rbu_cancel", BUS_ID_SIZE);
+
+	kobject_set_name(&rbu_device_mono.kobj, "%s", rbu_device_mono.bus_id);
+	kobject_set_name(&rbu_device_packet.kobj, "%s",
+			 rbu_device_packet.bus_id);
+	kobject_set_name(&rbu_device_cancel.kobj, "%s",
+			 rbu_device_cancel.bus_id);
+
+	rc = request_firmware_nowait(THIS_MODULE,
+				     "dell_rbu_mono", &rbu_device_mono,
+				     &context, callbackfn_mono);
+	if (rc)
+		printk(KERN_ERR "%s rbu_mono request_firmware_nowait"
+		       " failed %d\n", __FUNCTION__, rc);
+
+	rc = request_firmware_nowait(THIS_MODULE,
+				     "dell_rbu_packet", &rbu_device_packet,
+				     &context, callbackfn_packet);
+	if (rc)
+		printk(KERN_ERR "%s rbu_packet request_firmware_nowait"
+		       " failed %d\n", __FUNCTION__, rc);
+
+	rc = request_firmware_nowait(THIS_MODULE,
+				     "dell_rbu_cancel", &rbu_device_cancel,
+				     &context, callbackfn_cancel);
+	if (rc)
+		printk(KERN_ERR "%s rbu_cancel request_firmware_nowait"
+		       " failed %d\n", __FUNCTION__, rc);
+
+	return rc;
+}
+
+static __exit void dcdrbu_exit(void)
+{
+	spin_lock(&rbu_data.lock);
+	packet_empty_list();
+	img_update_free();
+	spin_unlock(&rbu_data.lock);
+}
+
+module_exit(dcdrbu_exit);
+module_init(dcdrbu_init);
diff -uprN /usr/src/linux-2.6.11.11.orig/drivers/firmware/Kconfig /usr/src/linux-2.6.11.11/drivers/firmware/Kconfig
--- /usr/src/linux-2.6.11.11.orig/drivers/firmware/Kconfig	2005-06-14 21:00:10.000000000 -0500
+++ /usr/src/linux-2.6.11.11/drivers/firmware/Kconfig	2005-06-14 20:59:56.000000000 -0500
@@ -58,4 +58,15 @@ config EFI_PCDP
 
 	  See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf>
 
+config DELL_RBU
+	tristate "BIOS update support for DELL systems via sysfs"
+	default y
+	select FW_LOADER
+	help
+	 Say Y if you want to have the option of updating the BIOS for your
+	 DELL system. Note you need a supporting application to comunicate
+	 with the BIOS regardin the new image for the image update to
+	 take effect.
+	See <file:Documentation/dell_rbu.txt> for more details on the driver.
+
 endmenu
diff -uprN /usr/src/linux-2.6.11.11.orig/drivers/firmware/Makefile /usr/src/linux-2.6.11.11/drivers/firmware/Makefile
--- /usr/src/linux-2.6.11.11.orig/drivers/firmware/Makefile	2005-06-14 21:01:11.000000000 -0500
+++ /usr/src/linux-2.6.11.11/drivers/firmware/Makefile	2005-06-14 21:00:47.000000000 -0500
@@ -4,3 +4,4 @@
 obj-$(CONFIG_EDD)             	+= edd.o
 obj-$(CONFIG_EFI_VARS)		+= efivars.o
 obj-$(CONFIG_EFI_PCDP)		+= pcdp.o
+obj-$(CONFIG_DELL_RBU)          += dell_rbu.o

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-06-02 23:58 ` Marcel Holtmann
  2005-06-03 12:32   ` Andreas Henriksson
@ 2005-06-05 21:51   ` Jesper Juhl
  1 sibling, 0 replies; 28+ messages in thread
From: Jesper Juhl @ 2005-06-05 21:51 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Abhay Salunke, linux-kernel, Andrew Morton, matt_domsch, Greg KH

On 6/3/05, Marcel Holtmann <marcel@holtmann.org> wrote:
> Hi Abhay,
> 
> > Resubmitting after cleaning up spaces/tabs etc...
> 
> and now starting with the coding style nitpicking ;)
> 
[snip]
> > +     if (pbuf != NULL) {
> 
> Make it "if (!pbuf)",
> 
[snip]

You want to make it  "if (pbuf)"  otherwise you'd be changing the logic.

-- 
Jesper Juhl <jesper.juhl@gmail.com>
Don't top-post  http://www.catb.org/~esr/jargon/html/T/top-post.html
Plain text mails only, please      http://www.expita.com/nomime.html

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-06-02 23:58 ` Marcel Holtmann
@ 2005-06-03 12:32   ` Andreas Henriksson
  2005-06-05 21:51   ` Jesper Juhl
  1 sibling, 0 replies; 28+ messages in thread
From: Andreas Henriksson @ 2005-06-03 12:32 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-kernel, Andrew Morton, matt_domsch, Greg KH

On Fri, Jun 03, 2005 at 01:58:48AM +0200, Marcel Holtmann wrote:

> 
> > +	if ((rc = fill_last_packet(data, length)) != 0)
> 
> Use "if (!(rc = fill_last_packet(data, length)))".
> 

Even better like this?

	rc = fill_last_packet(data, length);
	if (!rc)


> 
> > +		if ( rc == 0 )
> 
> Spaces.
> 

Plus use: if (!rc)


Regards,
Andreas Henriksson

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-06-02 23:26 Abhay Salunke
@ 2005-06-02 23:58 ` Marcel Holtmann
  2005-06-03 12:32   ` Andreas Henriksson
  2005-06-05 21:51   ` Jesper Juhl
  0 siblings, 2 replies; 28+ messages in thread
From: Marcel Holtmann @ 2005-06-02 23:58 UTC (permalink / raw)
  To: Abhay Salunke; +Cc: linux-kernel, Andrew Morton, matt_domsch, Greg KH

Hi Abhay,

> Resubmitting after cleaning up spaces/tabs etc...

and now starting with the coding style nitpicking ;)

> +	/* check if we have any packets */
> +	if (0 == rbu_data.num_packets) {

Make it "if (!rbu_data.num_packets) {".

> +	while(--packet_count) {
> +		ptemp_list = ptemp_list->next;
> +	}

Don't forget the space between "while" and "(" and the "{"/"}" are not
needed.

> +	ppacket = list_entry(ptemp_list,struct packet_data, list);

We always put a space after ",".

> +	if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {

Make it "(rbu_data.packet_write_count + length > rbu_data.packetsize)".

> +	/* copy the incoming data in to the new buffer */
> +	memcpy((ppacket->data + rbu_data.packet_write_count),
> +			data, length);

Make it "memcpy(ppacket->data + rbu_data.packet_write_count, ".

> +	if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
> +		/*
> +		 this was the last data chunk in the packet
> +		 so reinitialize the packet data counter to zero
> +		*/
> +		rbu_data.packet_write_count = 0;
> +	} else
> +		rbu_data.packet_write_count += length;

Why not:

	rbu_data.packet_write_count += length;
	if (rbu_data.packet_write_count == rbu_data.packetsize)
		rbu_data.packet_write_count = 0;

> +	/* try allocating a new buffer to fit the request */
> +	pbuf =(unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);

Forgot a space after "=" and after "...char*)".

> +	if (pbuf != NULL) {

Make it "if (!pbuf)",

> +		/* check if the image is with in limits */
> +		img_buf_phys_addr = (unsigned long)virt_to_phys(pbuf);

Put a space after "...long)".

> +		if ((limit != 0) && ((img_buf_phys_addr + size) > limit)) {

Make it "if (!limit && img_bug_phys_addr + size > limit)"

> +			free_pages ((unsigned long)pbuf, *ordernum);

Space.

> +			 Try allocating a new buffer from the 
> +			 GFP_DMA range as it is with in 16MB range.
> +			*/
> +			pbuf =(unsigned char *)__get_free_pages(GFP_DMA,

Space.

> +			if (pbuf == NULL)

Use "(!pbuf)".

> +	if (rbu_data.packetsize == 0 ) {

Use "if (!rbu_data.packetsize)".

> +	if(newpacket == NULL) {

Use "if (!newpacket)".

> +	if(newpacket->data == NULL) {

Use "if (!newpacket->data)"

> +	if (rbu_data.packet_write_count == 0) {
> +		if ((rc = create_packet(length)) != 0 )
> +			return rc;
> +	}

Use this:

	if (!rbu_data.packet_write_count)
		if (!(rc = create_packet(length)))
			return rc;

> +	if ((rc = fill_last_packet(data, length)) != 0)

Use "if (!(rc = fill_last_packet(data, length)))".

> +		free_pages((unsigned long)newpacket->data, newpacket->ordernum);

Space.

> +	if (rbu_data.image_update_buffer == NULL)

Use "(!rbu_data.image_update_buffer)".

> +	free_pages((unsigned long)rbu_data.image_update_buffer,

Space.

> +		if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {

Use "if (!size && !rbu_data.image_update_buffer)"

> +	image_update_buffer = (unsigned char *)get_free_pages_limited(size,

Space.

> +	if (image_update_buffer != NULL) {

Use "if (!image_update_buffer)".

> +		memset(rbu_data.image_update_buffer,0,

Space.

> +	sscanf(buf, "%d",&size);	

Spaces.

> +	if (size != 0)

Use "if (!size)".

> +	if (type == MONOLITHIC )

Space.

> +	if ( type == MONOLITHIC )

Spaces.

> +		size = sprintf(buf, "%lu\n",  rbu_data.bios_image_size);

Extra space not needed.

> +	if (type == MONOLITHIC ) 

Space.

> +	if (strlen(buf) >= 256 ) {

Space.

> +	if (type == MONOLITHIC ) {

Space.

> +	if (type == MONOLITHIC ) {

Space.

> +		if (fw_entry->size == 0 )

use "if (!fw_entry->size)".

> +				if (rc == 0) {

Use "if (!rc)".

> +		if ( rbu_data.packetsize != fw_entry->size )

Spaces.

> +		if ( rc == 0 )

Spaces.

> +static decl_subsys(dell_rbu,&ktype_dell_rbu,NULL);

Spaces.

> +        memset(rbu_dev, 0, sizeof (*rbu_dev));

No tab.

> +	if (type == MONOLITHIC)

Space.

> +		if (type == MONOLITHIC ) {

Space.

> +	if (rbu_dev != NULL) {

Use "if (!rbu_dev)".

> +static int __init dcdrbu_init(void)

What stand "dcd" for? Why not only "rbu_init"?

> +	if (rbu_download_mono == NULL) {	

Use "if (!rbu_download)".

> +	rbu_download_packet=  create_rbu_download_entry (PACKETIZED);

Wrong spaces.

> +	if (rbu_download_packet == NULL) {	

Use "if (!rbu_download_packet)".

> +	strncpy(rbu_device.bus_id,"firmware", BUS_ID_SIZE);

Space.

> +static __exit void dcdrbu_exit( void)

Why "dcd"?

> +config DELL_RBU
> +        tristate "BIOS update support for DELL systems via sysfs"
> +        default n
> +	select FW_LOADER

Space vs tab clash.

Regards

Marcel



^ permalink raw reply	[flat|nested] 28+ messages in thread

* [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-06-02 23:26 Abhay Salunke
  2005-06-02 23:58 ` Marcel Holtmann
  0 siblings, 1 reply; 28+ messages in thread
From: Abhay Salunke @ 2005-06-02 23:26 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton; +Cc: abhay_salunke, matt_domsch, Greg KH, marcel

Resubmitting after cleaning up spaces/tabs etc...

By making a contribution to this project, I certify that:
The contribution was created in whole or in part by me and I have the
right to submit it under the open source license indicated in the file.
Resubmitting after cleaning up spaces/tabs etc...

Signed-off-by: Abhay Salunke <Abhay_Salunke@dell.com>

Thanks,
Abhay Salunke
Software Engineer.
DELL Inc

diff -uprN linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt linux-2.6.11.8/Documentation/DELL_RBU.txt
--- linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.8/Documentation/DELL_RBU.txt	2005-06-02 13:23:36.000000000 -0500
@@ -0,0 +1,70 @@
+Purpose:
+Demonstrate the usage of the DELL_RBU (DELL Remote BIOS Update) driver
+for updating BIOS images on Dell hardware.
+
+Scope:
+This document discusses the functionality of the DELL_RBU driver. 
+This driver is required by BIOS update applications shipped by DELL for updating
+BIOS on DELL servers and client systems. 
+
+Overview:
+The rbu driver is designed to be running on 2.6 kernel. 
+This driver is one single dell_rbu.c file (approx 800 lines total).
+This driver utilizes the hotplug interface for downloading the BIOS update image 
+in the contiguous or packetized memory depending upon the update type.
+The BIOS then scans the memory to find the image and will then update itself. 
+There are basically two different mechanisms for writing the BIOS image in to 
+contiguous memory 
+1> By writing the image to one monolithic chunk of contiguous physical memory.
+2> By writing image in to smaller packet chunks of contiguous physical memory.
+The update mechanism is determined by the update application based on the 
+particular system type.
+
+Update mechanism using single physical chunk of memory:
+The rbu driver on its load time created the following entries in sysfs
+/sys/firmware/dell_rbu/monolithic/mono_name
+/sys/firmware/dell_rbu/monolithic/mono_size
+/sys/firmware/dell_rbu/packetized/packet_name
+/sys/firmware/dell_rbu/packetized/packet_size
+
+Steps to update the BIOS image:
+
+1> Copy the image file in to /lib/firmware 
+2> echo the image name in to /sys/firmware/dell_rbu/xxxx/xxxx_name
+
+This will generate a hotplug event and the data form the image file is
+transferred to the memory. 
+Here xxxx stands for the type of BIOS update mechanism chosen by choosing
+monolitich the image is copied to contiguous physical pages and by choosing
+the mechanism as packetized the image is treated as one single packet and the
+packet size is set to the first packet image size. If a new image packet of
+different size form the previous is copied then all the previous packets are
+freed and this packet's size is treated as new packet size.
+
+On a driver unload all the allocated memory is freed.
+The user should not unload the driver after downloading the new BIOS image  
+if it wants to update BIOS with that image.
+The user can overwrite the previously loaded monolithich image by echoing a 
+new file name string to /sys/firmware/dell_rbu/monolithic/mono_name. Make sure 
+the file is present in /lib/firmware.  If the image size is more than
+previous image then the previous image is freed and the new alocation is made. 
+
+The user can know of a successful allocation by readind the size files.
+cat /sys/firmware/dell_rbu/monolithic/mono_size
+
+Update using smaller chunks (packets) of contiguous memory:
+The disadvantage of contiguous allocation is that it may not be always possible
+to get that size of contiuguous chunk of avaliable physical pages as in most 
+Linux systems the memory gets fragmented immideately after a reboot.
+The update using smaller chunks fixes this issue; it also requires the BIOS on 
+the system to support this feature; the update application needs to query this 
+with the BIOS on the system before using this technique. 
+
+
+NOTE:
+Afte updating the BIOS image the appplication needs to communicate with the BIOS 
+for enabling the update on the next reboot. The application can then choose to 
+reboot the system imideately or not reboot the system and leave up to the user 
+to do a reboot.
+
+
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c linux-2.6.11.8/drivers/firmware/dell_rbu.c
--- linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.8/drivers/firmware/dell_rbu.c	2005-06-02 18:18:24.035903088 -0500
@@ -0,0 +1,680 @@
+/*
+ * dell_rbu.c
+ * Bios Update driver for Dell systems
+ * Author: Dell Inc
+ *	   Abhay Salunke <abhay_salunke@dell.com>
+ *
+ * Copyright (C) 2005 Dell Inc.
+ *
+ * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by creating 
+ * entries in the /sys file systems on Linux 2.6 and higher kernels.
+ * It uses the hotplug interface for getting the image in to memory.
+ * The driver supports two mechanism to update the BIOS namely contiguous and 
+ * packetized. Both these methods still require having some application to set
+ * the CMOS bit indicating the BIOS to update itself after a reboot.
+ * In both the methods the image file name needs to be specified for hotplugging
+ * the image file. 
+ *
+ * Contiguous method:
+ * This driver writes the incoming data in a monolithic image by allocating 
+ * contiguous physical pages large enough to accommodate the incoming BIOS image 
+ * size.  
+ *
+ * Packetized method:
+ * The driver writes the incoming packet image by allocating a new packet on 
+ * every time the packet image name is written. 
+ * This driver requires an application to break the BIOS image in to fixed sized 
+ * packet chunks and each packet is written as a hotplug image.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation
+ *
+ * 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.
+ */
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+
+MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
+MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+#define BIOS_SCAN_LIMIT 0xffffffff
+#define MONOLITHIC (1)
+#define PACKETIZED (2)
+
+static struct _rbu_data {
+        void *image_update_buffer;
+        unsigned long image_update_buffer_size;
+        unsigned long bios_image_size;
+        unsigned long image_update_order_number;
+        spinlock_t lock;
+        unsigned long packet_read_count;
+        unsigned long packet_write_count;
+        unsigned long num_packets;
+        unsigned long packetsize;
+        char mono_image[256];
+        char packet_image[256];
+} rbu_data;
+
+struct packet_data{
+	struct list_head list;
+	size_t length;
+	void *data;
+	int ordernum;
+};
+
+static struct packet_data packet_data_head;
+
+static void init_packet_head(void)
+{
+	INIT_LIST_HEAD(&packet_data_head.list);
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+struct rbu_download_device {
+	int type;
+	struct kobject kobj;
+};
+
+static struct rbu_download_device *rbu_download_mono;
+
+static struct rbu_download_device *rbu_download_packet;
+
+static struct device rbu_device;
+
+static int fill_last_packet(void *data, size_t length)
+{
+	struct list_head *ptemp_list;
+	struct packet_data *ppacket = NULL;
+	int packet_count = 0;
+
+	pr_debug("fill_last_packet: entry \n");
+
+	/* check if we have any packets */
+	if (0 == rbu_data.num_packets) {
+		pr_debug("fill_last_packet: num_packets=0\n");
+		return -ENOMEM;
+	}
+
+	packet_count = rbu_data.num_packets;
+
+	ptemp_list = (&packet_data_head.list)->next;
+
+	while(--packet_count) {
+		ptemp_list = ptemp_list->next;
+	}
+
+	ppacket = list_entry(ptemp_list,struct packet_data, list);
+
+	if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
+		printk(KERN_WARNING "fill_last_packet: packet size data "
+			"overrun\n");
+		return -ENOMEM;
+	}
+
+	pr_debug("fill_last_packet : buffer = %p\n", ppacket->data);
+
+	/* copy the incoming data in to the new buffer */
+	memcpy((ppacket->data + rbu_data.packet_write_count),
+			data, length);
+
+	if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
+		/*
+		 this was the last data chunk in the packet
+		 so reinitialize the packet data counter to zero
+		*/
+		rbu_data.packet_write_count = 0;
+	} else
+		rbu_data.packet_write_count += length;
+	
+	pr_debug("fill_last_packet: exit \n");
+	return 0;
+}
+
+/*
+ get_free_pages_limited:
+ This is a helper function which allocates free pages based on an upper limit.
+ On x86_64 or 64 bit arch the memory allocation goes above 4GB space which is
+ not addressable by the BIOS. This function tries to get allocation below the
+ limit (4GB) address. It first tries to allocate memory normally using the
+ GFP_KERNEL argument if the incoming limit is non-zero and if the returned
+ physical memory address exceeds the upper limit, the allocated pages are freed
+ and the memory is reallocated using the GFP_DMA argument.
+*/
+static void *get_free_pages_limited(unsigned long size,
+                                    int *ordernum,
+                                    unsigned long limit)
+{
+	unsigned long img_buf_phys_addr;
+	void *pbuf = NULL;
+
+	*ordernum = get_order(size);
+	/*
+	 Check if we are not getting a very large file.
+	 This can happen as a user error in entering the file size
+	*/
+	if (*ordernum == BITS_PER_LONG) {
+		pr_debug("get_free_pages_limited: Incoming size is"
+			" very large\n");
+		return NULL;
+	}
+
+	/* try allocating a new buffer to fit the request */
+	pbuf =(unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);
+
+	if (pbuf != NULL) {
+		/* check if the image is with in limits */
+		img_buf_phys_addr = (unsigned long)virt_to_phys(pbuf);
+
+		if ((limit != 0) && ((img_buf_phys_addr + size) > limit)) {
+			pr_debug("Got memory above 4GB range, free this "
+				"and try with DMA memory\n");
+			/*
+			  free this memory as we need it with in 
+			 4GB range 
+			*/
+			free_pages ((unsigned long)pbuf, *ordernum);
+			/*
+			 Try allocating a new buffer from the 
+			 GFP_DMA range as it is with in 16MB range.
+			*/
+			pbuf =(unsigned char *)__get_free_pages(GFP_DMA,
+				*ordernum);
+			if (pbuf == NULL)
+				pr_debug("Failed to get memory "
+					"of size %ld "
+					"using GFP_DMA\n", size);
+		}
+	}
+	return pbuf;
+}
+
+static int create_packet(size_t length)
+{
+	struct packet_data *newpacket;
+	int ordernum = 0;
+
+	pr_debug("create_packet: entry \n");
+
+	if (rbu_data.packetsize == 0 ) {
+		pr_debug("create_packet: packetsize not specified\n");
+		return -EINVAL;
+	}
+
+	newpacket = kmalloc(sizeof(struct packet_data) ,GFP_KERNEL);
+	if(newpacket == NULL) {
+		printk(KERN_WARNING "create_packet: failed to allocate new "
+			"packet\n");
+		return -ENOMEM;
+	}
+
+	/* there is no upper limit on memory address for packetized mechanism*/
+	newpacket->data = get_free_pages_limited(rbu_data.packetsize,
+							&ordernum, 0);
+	pr_debug("create_packet: newpacket %p\n", newpacket->data);
+
+	if(newpacket->data == NULL) {
+		printk(KERN_WARNING "create_packet: failed to allocate new "
+			"packet\n");
+		kfree(newpacket);
+		return -ENOMEM;
+	}
+
+	newpacket->ordernum = ordernum;
+	++rbu_data.num_packets;
+	/* initialize the newly created packet headers */
+	INIT_LIST_HEAD(&newpacket->list);
+	list_add_tail(&newpacket->list, &packet_data_head.list);
+	/* packets have fixed size*/
+	newpacket->length = rbu_data.packetsize;
+
+	pr_debug("create_packet: exit \n");
+
+	return 0;
+}
+
+static int packetize_data(void *data, size_t length)
+{
+	int rc = 0;
+
+	if (rbu_data.packet_write_count == 0) {
+		if ((rc = create_packet(length)) != 0 )
+			return rc;
+	}
+	/* fill data in to the packet */
+	if ((rc = fill_last_packet(data, length)) != 0)
+		return rc;
+
+	return rc;
+}
+
+static void packet_empty_list(void)
+{
+	struct list_head *ptemp_list;
+	struct list_head *pnext_list;
+	struct packet_data *newpacket;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while(!list_empty(ptemp_list)) {
+		newpacket = list_entry(ptemp_list, struct packet_data, list);
+		pnext_list = ptemp_list->next;
+		list_del(ptemp_list);
+		ptemp_list = pnext_list;
+		/*
+		 zero out the RBU packet memory before freeing to make sure
+		 there are no stale RBU packets left in memory
+		*/
+		memset(newpacket->data, 0, rbu_data.packetsize);
+		free_pages((unsigned long)newpacket->data, newpacket->ordernum);
+		kfree(newpacket);
+	}
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+/*
+ img_update_free:
+ Frees the buffer allocated for storing BIOS image
+ Always called with lock held and returned with lock held
+*/
+static void img_update_free( void)
+{
+	if (rbu_data.image_update_buffer == NULL)
+		return;
+	/*
+	 zero out this buffer before freeing it to get rid of any stale
+	 BIOS image copied in memory.
+	*/
+	memset(rbu_data.image_update_buffer, 0,
+		rbu_data.image_update_buffer_size);
+	free_pages((unsigned long)rbu_data.image_update_buffer,
+		rbu_data.image_update_order_number);
+	/* Re-initialize the rbu_data variables after a free */
+	rbu_data.image_update_buffer = NULL;
+	rbu_data.image_update_buffer_size = 0;
+	rbu_data.bios_image_size = 0;
+}
+
+/*
+ img_update_realloc:
+ This function allocates the contiguous pages to accommodate the requested
+ size of data. The memory address and size values are stored globally and
+ on every call to this function the new size is checked to see if more
+ data is required than the existing size. If true the previous memory is
+ freed and new allocation is done to accommodate the new size. If the 
+ incoming size is less then than the already allocated size, then that
+ memory is reused.
+ This function is called with lock held and returns with lock held.
+*/
+static int img_update_realloc(unsigned long size)
+{
+	unsigned char *image_update_buffer = NULL;
+	unsigned long rc;
+	int ordernum =0;
+
+	/* check if the buffer of sufficient size has been already allocated */
+	if (rbu_data.image_update_buffer_size >= size) {
+		/* check for corruption */
+		if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {
+			printk(KERN_ERR "img_update_realloc: "
+				"corruption check failed\n");
+			return -EINVAL;
+		}
+		/* we have a valid pre-allocated buffer with sufficient size */
+		return 0;
+	}
+
+	/* free any previously allocated buffer */
+	img_update_free();
+	
+	/*
+	 This has already been called as locked so we can now unlock
+	 and proceed to calling get_free_pages_limited as this function
+	 can sleep
+	*/
+	spin_unlock(&rbu_data.lock);
+
+	image_update_buffer = (unsigned char *)get_free_pages_limited(size,
+		&ordernum,
+		BIOS_SCAN_LIMIT);
+
+	/* acquire the spinlock again */
+	spin_lock(&rbu_data.lock);
+
+	if (image_update_buffer != NULL) {
+		rbu_data.image_update_buffer = image_update_buffer;
+		rbu_data.image_update_buffer_size = PAGE_SIZE << ordernum;
+		rbu_data.image_update_order_number = ordernum;
+		memset(rbu_data.image_update_buffer,0,
+			rbu_data.image_update_buffer_size);
+		rc = 0;
+	} else {
+		pr_debug("Not enough memory for image update:order"
+			" number = %d,size = %ld\n",ordernum, size);
+		rc = -ENOMEM;
+	}
+
+	return rc;
+} /* img_update_realloc */
+
+/*
+ rbu_store_image_size:
+ This is primarily for cancelling any RBU updates.
+*/
+static ssize_t rbu_store_image_size (struct rbu_download_device *rbu_dev,
+		const char *buf, size_t count, int type)
+{
+	int size = 0;
+	
+	sscanf(buf, "%d",&size);	
+	if (size != 0)
+		return -EINVAL;
+
+	spin_lock(&rbu_data.lock);
+	if (type == MONOLITHIC )
+		img_update_free();
+	else 
+		packet_empty_list();
+
+	spin_unlock(&rbu_data.lock);
+	return count; 
+}
+
+static ssize_t rbu_show_image_size (struct rbu_download_device *rbu_dev, 
+		char *buf, int type)
+{
+	unsigned int size = 0;
+	if ( type == MONOLITHIC )
+		size = sprintf(buf, "%lu\n",  rbu_data.bios_image_size);
+	else
+		size = sprintf(buf, "%lu\n", rbu_data.packetsize);
+	return size;
+}
+
+static ssize_t rbu_show_image_name (struct rbu_download_device *rbu_dev, 
+		char *buf, int type)
+{
+	unsigned int size = 0;
+	char *image;
+        
+	if (type == MONOLITHIC ) 
+		image = rbu_data.mono_image;
+	else
+		image = rbu_data.packet_image;
+	size = sprintf(buf, "%s\n", image);
+	return size;
+}
+
+static ssize_t rbu_store_image_name (struct rbu_download_device *rbu_dev, 
+		const char *buf, size_t count, int type)
+{
+	int rc = count;
+	const struct firmware *fw_entry;
+	char *image_name = NULL;
+
+	spin_lock(&rbu_data.lock);
+
+	if (strlen(buf) >= 256 ) {
+		spin_unlock(&rbu_data.lock);
+		rc = -ENOMEM;
+	}
+
+	if (type == MONOLITHIC ) {
+		sscanf(buf, "%s",rbu_data.mono_image);
+		image_name = rbu_data.mono_image;
+	} else {
+		sscanf(buf, "%s",rbu_data.packet_image);
+		image_name = rbu_data.packet_image;
+	}
+
+	spin_unlock(&rbu_data.lock);	
+
+	rc = request_firmware(&fw_entry, image_name,
+		&rbu_device);
+	if (rc) {
+		printk(KERN_ERR "rbu_store_image_name: " 
+			"Firmware not available %d\n", rc);
+		return rc;
+	}
+		
+	pr_debug("rbu_store_image_name: request_firmware is successful "
+		"fw->size = %lu\n", fw_entry->size);
+
+	spin_lock(&rbu_data.lock);
+	if (type == MONOLITHIC ) {
+		rbu_data.bios_image_size = fw_entry->size;
+		if (fw_entry->size == 0 )
+			img_update_free();
+		else {	
+			rc = img_update_realloc(fw_entry->size);
+				if (rc == 0) {
+					memcpy(rbu_data.image_update_buffer, 
+						fw_entry->data, 
+						fw_entry->size);
+					rc = count;
+				}
+			}
+	} else {
+		/* 
+		  if a new packet is entered free all previsou 
+		  packets and start over.
+		 */
+		if ( rbu_data.packetsize != fw_entry->size )
+			packet_empty_list();
+
+		rbu_data.packetsize = fw_entry->size;
+		rc = packetize_data(fw_entry->data, fw_entry->size);
+		if ( rc == 0 )
+			rc = count;
+	}
+	spin_unlock(&rbu_data.lock);
+	release_firmware(fw_entry);
+
+	return rc;
+}
+
+/* no default attributes yet. */
+static struct attribute * def_attrs[] = { NULL, };
+
+struct rbu_attribute {
+	struct attribute attr;
+	ssize_t (*show) (struct rbu_download_device *rbu_dev,
+			char *buf, int type);
+	ssize_t (*store)(struct rbu_download_device *rbu_dev, 
+			const char *buf , size_t count, int type);
+	int type;
+};
+
+#define RBU_DEVICE_ATTR(_name,_mode,_show,_store, _type) \
+struct rbu_attribute rbu_attr_##_name = {       \
+	.attr ={.name= __stringify(_name), .mode= _mode, .owner= THIS_MODULE},\
+	.show = _show,                                \
+	.store = _store,                                \
+	.type = _type,	 \
+};
+
+#define to_rbu_attr(_attr) container_of(_attr,struct rbu_attribute,attr)
+#define to_rbu_download_device(obj)  \
+	container_of(obj,struct rbu_download_device,kobj)
+
+
+static RBU_DEVICE_ATTR(mono_name, 0644, rbu_show_image_name, 
+			rbu_store_image_name, MONOLITHIC);
+
+static RBU_DEVICE_ATTR(mono_size, 0644, rbu_show_image_size, 
+			rbu_store_image_size, MONOLITHIC);
+
+static RBU_DEVICE_ATTR(packet_name, 0644, rbu_show_image_name,
+			rbu_store_image_name, PACKETIZED);
+
+static RBU_DEVICE_ATTR(packet_size, 0644, rbu_show_image_size,
+			rbu_store_image_size, PACKETIZED);
+
+static  ssize_t rbu_attr_store (struct kobject *kobj,
+				struct attribute *attr,
+				const char *buf, 
+				size_t count) 
+{
+	struct rbu_download_device *rbu_dev = to_rbu_download_device(kobj);
+	struct rbu_attribute *rbu_attr = to_rbu_attr(attr);
+	ssize_t rc = count;
+
+	pr_debug("rbu_attr_store: entry type = %d\n", rbu_attr->type);
+	
+	if (rbu_attr->store)
+		rc = rbu_attr->store(rbu_dev, buf, count, rbu_attr->type);
+	return rc;
+}
+
+static ssize_t rbu_attr_show (struct kobject * kobj, 
+			      struct attribute *attr, 
+			      char *buf)
+{
+	struct rbu_download_device *rbu_dev = to_rbu_download_device(kobj);
+	struct rbu_attribute *rbu_attr = to_rbu_attr(attr);
+	ssize_t rc = 0;
+	pr_debug("rbu_attr_show: entry type = %d\n", rbu_attr->type);
+	if (rbu_attr->show)
+		rc = rbu_attr->show(rbu_dev, buf, rbu_attr->type);
+	return rc;
+}
+
+static struct sysfs_ops rbu_attr_ops = { 
+	.show = rbu_attr_show,
+	.store = rbu_attr_store,
+};
+
+static struct kobj_type ktype_dell_rbu = { 
+	.sysfs_ops	= &rbu_attr_ops,
+	.default_attrs	= def_attrs,
+};
+static decl_subsys(dell_rbu,&ktype_dell_rbu,NULL);
+
+static int rbu_download_device_register(struct rbu_download_device *rbu_dev, 
+					int type)
+{
+	int rc = 0;
+	if (!rbu_dev)
+		return 1;
+        memset(rbu_dev, 0, sizeof (*rbu_dev));
+	if (type == MONOLITHIC)
+		kobject_set_name(&rbu_dev->kobj, "monolithic");
+	else 
+		kobject_set_name(&rbu_dev->kobj, "packetized");
+	kobj_set_kset_s(rbu_dev,dell_rbu_subsys);
+	rc = kobject_register(&rbu_dev->kobj);
+	if (!rc) {
+		if (type == MONOLITHIC ) {
+			sysfs_create_file(&rbu_dev->kobj,
+				&rbu_attr_mono_name.attr);
+			sysfs_create_file(&rbu_dev->kobj,
+				&rbu_attr_mono_size.attr);
+		} else {
+			sysfs_create_file(&rbu_dev->kobj,
+				&rbu_attr_packet_name.attr);
+			sysfs_create_file(&rbu_dev->kobj,
+				&rbu_attr_packet_size.attr);
+		}
+	} else 
+		pr_debug("rbu_download_device_register: "
+			"kobject_register %d \n", rc);
+	pr_debug("rbu_download_device_register: rbu_dev addr %p\n", rbu_dev);
+	return rc;
+}
+
+static struct rbu_download_device *create_rbu_download_entry(int type) 
+{
+	struct rbu_download_device *rbu_dev = NULL;
+	rbu_dev = kmalloc( sizeof(struct rbu_download_device), 
+		GFP_KERNEL);
+	if (!rbu_dev) {
+		printk(KERN_ERR "create_rbu_download_entry: kmalloc failed\n");
+		return NULL;
+	}
+	rbu_dev->type = type;
+	if (rbu_download_device_register(rbu_dev, type)) {
+		pr_debug("create_rbu_download_entry: "
+			"rbu_download_device_register failed \n");
+		kfree(rbu_dev);
+        }
+
+	pr_debug("create_rbu_download_entry: rbu_dev %p\n", rbu_dev);	
+	return rbu_dev;
+}
+
+static void remove_rbu_download_entry(struct rbu_download_device *rbu_dev, 
+				       int type) 
+{
+	pr_debug("remove_rbu_download_entry: rbu_dev ptr %p \n", rbu_dev);	
+	if (rbu_dev != NULL) {
+		kobject_unregister(&rbu_dev->kobj);
+		kfree(rbu_dev);
+	}
+}
+
+static int __init dcdrbu_init(void)
+{
+	int rc = 0;
+	spin_lock_init(&rbu_data.lock);
+	
+	init_packet_head();
+	
+	device_initialize(&rbu_device);
+
+	rc = firmware_register(&dell_rbu_subsys);
+	if (rc < 0) {
+		printk(KERN_WARNING "dcdrbu_init: firmware_register"
+			" dell_rbu failed\n");
+		return rc;
+        }
+	
+	rbu_download_mono = create_rbu_download_entry (MONOLITHIC);
+	if (rbu_download_mono == NULL) {	
+		firmware_unregister(&dell_rbu_subsys);
+		return -ENOMEM;
+	}
+	
+	rbu_download_packet=  create_rbu_download_entry (PACKETIZED);
+	if (rbu_download_packet == NULL) {	
+		remove_rbu_download_entry(rbu_download_mono, MONOLITHIC);
+		firmware_unregister(&dell_rbu_subsys);
+		return -ENOMEM;
+	}
+	strncpy(rbu_device.bus_id,"firmware", BUS_ID_SIZE);
+	return rc;
+}
+
+static __exit void dcdrbu_exit( void)
+{
+	spin_lock(&rbu_data.lock);
+	packet_empty_list();
+	img_update_free();
+	spin_unlock(&rbu_data.lock);
+	remove_rbu_download_entry(rbu_download_packet, PACKETIZED);
+	remove_rbu_download_entry(rbu_download_mono, MONOLITHIC);
+	firmware_unregister(&dell_rbu_subsys);
+}
+
+module_exit(dcdrbu_exit);
+module_init(dcdrbu_init);
+
+
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Kconfig linux-2.6.11.8/drivers/firmware/Kconfig
--- linux-2.6.11.8.ORIG/drivers/firmware/Kconfig	2005-05-13 12:07:58.000000000 -0500
+++ linux-2.6.11.8/drivers/firmware/Kconfig	2005-06-02 17:01:29.072484168 -0500
@@ -58,4 +58,17 @@ config EFI_PCDP
 
 	  See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf>
 
+config DELL_RBU
+        tristate "BIOS update support for DELL systems via sysfs"
+        default n
+	select FW_LOADER
+        help
+          Say Y if you want to have the option of updating the BIOS for your
+	  DELL system. Note you need a supporting application to comunicate 
+	  with the BIOS regardin the new image for the image update to 
+	  take effect.
+
+	  See <file:Documentation/DELL_RBU.txt> for more details on the driver.
+
+
 endmenu
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Makefile linux-2.6.11.8/drivers/firmware/Makefile
--- linux-2.6.11.8.ORIG/drivers/firmware/Makefile	2005-05-13 12:08:12.000000000 -0500
+++ linux-2.6.11.8/drivers/firmware/Makefile	2005-05-09 15:15:16.000000000 -0500
@@ -4,3 +4,4 @@
 obj-$(CONFIG_EDD)             	+= edd.o
 obj-$(CONFIG_EFI_VARS)		+= efivars.o
 obj-$(CONFIG_EFI_PCDP)		+= pcdp.o
+obj-$(CONFIG_DELL_RBU)		+= dell_rbu.o

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-06-02 18:36 Abhay Salunke
@ 2005-06-02 21:44 ` Marcel Holtmann
  0 siblings, 0 replies; 28+ messages in thread
From: Marcel Holtmann @ 2005-06-02 21:44 UTC (permalink / raw)
  To: Abhay Salunke; +Cc: linux-kernel, Andrew Morton, matt_domsch, Greg KH

Hi Abhay,

> This is a resubmit of the patch after incorporating all the inputs from revieweres. 
> This has the hotplug firmware interface as suggested by many. 
> Currently it does not suport reading back the data; I am workingon it and will add 
> that feature as new patch.

please fix the coding style. We use tabs instead of spaces.

Make sure that all functions are static and clean your namespace. Even
if they are static it is still unclean.

It is <linux/firmware.h> and not "linux/firmware.h".

The Kconfig is missing a "select FW_LOADER".

Regards

Marcel



^ permalink raw reply	[flat|nested] 28+ messages in thread

* [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-06-02 18:36 Abhay Salunke
  2005-06-02 21:44 ` Marcel Holtmann
  0 siblings, 1 reply; 28+ messages in thread
From: Abhay Salunke @ 2005-06-02 18:36 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton; +Cc: abhay_salunke, matt_domsch, Greg KH

This is a resubmit of the patch after incorporating all the inputs from revieweres. 
This has the hotplug firmware interface as suggested by many. 
Currently it does not suport reading back the data; I am workingon it and will add 
that feature as new patch.

By making a contribution to this project, I certify that:
The contribution was created in whole or in part by me and I have the 
right to submit it under the open source license indicated in the file.

Signed-off-by: Abhay Salunke <Abhay_Salunke@dell.com>

Thanks,
Abhay Salunke
Software Engineer.
DELL Inc

diff -uprN linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt linux-2.6.11.8/Documentation/DELL_RBU.txt
--- linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.8/Documentation/DELL_RBU.txt	2005-06-02 13:23:36.358562176 -0500
@@ -0,0 +1,70 @@
+Purpose:
+Demonstrate the usage of the DELL_RBU (DELL Remote BIOS Update) driver
+for updating BIOS images on Dell hardware.
+
+Scope:
+This document discusses the functionality of the DELL_RBU driver. 
+This driver is required by BIOS update applications shipped by DELL for updating
+BIOS on DELL servers and client systems. 
+
+Overview:
+The rbu driver is designed to be running on 2.6 kernel. 
+This driver is one single dell_rbu.c file (approx 800 lines total).
+This driver utilizes the hotplug interface for downloading the BIOS update image 
+in the contiguous or packetized memory depending upon the update type.
+The BIOS then scans the memory to find the image and will then update itself. 
+There are basically two different mechanisms for writing the BIOS image in to 
+contiguous memory 
+1> By writing the image to one monolithic chunk of contiguous physical memory.
+2> By writing image in to smaller packet chunks of contiguous physical memory.
+The update mechanism is determined by the update application based on the 
+particular system type.
+
+Update mechanism using single physical chunk of memory:
+The rbu driver on its load time created the following entries in sysfs
+/sys/firmware/dell_rbu/monolithic/mono_name
+/sys/firmware/dell_rbu/monolithic/mono_size
+/sys/firmware/dell_rbu/packetized/packet_name
+/sys/firmware/dell_rbu/packetized/packet_size
+
+Steps to update the BIOS image:
+
+1> Copy the image file in to /lib/firmware 
+2> echo the image name in to /sys/firmware/dell_rbu/xxxx/xxxx_name
+
+This will generate a hotplug event and the data form the image file is
+transferred to the memory. 
+Here xxxx stands for the type of BIOS update mechanism chosen by choosing
+monolitich the image is copied to contiguous physical pages and by choosing
+the mechanism as packetized the image is treated as one single packet and the
+packet size is set to the first packet image size. If a new image packet of
+different size form the previous is copied then all the previous packets are
+freed and this packet's size is treated as new packet size.
+
+On a driver unload all the allocated memory is freed.
+The user should not unload the driver after downloading the new BIOS image  
+if it wants to update BIOS with that image.
+The user can overwrite the previously loaded monolithich image by echoing a 
+new file name string to /sys/firmware/dell_rbu/monolithic/mono_name. Make sure 
+the file is present in /lib/firmware.  If the image size is more than
+previous image then the previous image is freed and the new alocation is made. 
+
+The user can know of a successful allocation by readind the size files.
+cat /sys/firmware/dell_rbu/monolithic/mono_size
+
+Update using smaller chunks (packets) of contiguous memory:
+The disadvantage of contiguous allocation is that it may not be always possible
+to get that size of contiuguous chunk of avaliable physical pages as in most 
+Linux systems the memory gets fragmented immideately after a reboot.
+The update using smaller chunks fixes this issue; it also requires the BIOS on 
+the system to support this feature; the update application needs to query this 
+with the BIOS on the system before using this technique. 
+
+
+NOTE:
+Afte updating the BIOS image the appplication needs to communicate with the BIOS 
+for enabling the update on the next reboot. The application can then choose to 
+reboot the system imideately or not reboot the system and leave up to the user 
+to do a reboot.
+
+
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c linux-2.6.11.8/drivers/firmware/dell_rbu.c
--- linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.8/drivers/firmware/dell_rbu.c	2005-06-02 13:28:48.821060656 -0500
@@ -0,0 +1,700 @@
+/*
+ * dell_rbu.c
+ * Bios Update driver for Dell systems
+ * Author: Dell Inc
+ *	   Abhay Salunke <abhay_salunke@dell.com>
+ *
+ * Copyright (C) 2005 Dell Inc.
+ *
+ * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by creating 
+ * entries in the /sys file systems on Linux 2.6 and higher kernels.
+ * It uses the hotplug interface for getting the image in to memory.
+ * The driver supports two mechanism to update the BIOS namely contiguous and 
+ * packetized. Both these methods still require to have some application to set
+ * the  CMOS bit indicating the BIOS to update itself after a reboot.
+ * In both the methods the image file name needs to be specified for hotplugging
+ * the image file. 
+ *
+ * Contiguous method:
+ * This driver writes the incmoing data in a monolithic image by allocating 
+ * contiguos physical pages large enough to accomodate the incoming BIOS image 
+ * size.  
+ *
+ * Packetized method:
+ * The driver writes the incoming packet image by allocating a new packet on 
+ * every time the packet image name is written. 
+ * This driver requires an application to break the BIOS image in to fixed sized 
+ * packet chunks and each packet is written as a hotplug image.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation
+ *
+ * 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.
+ */
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include "linux/firmware.h"
+#define BIOS_SCAN_LIMIT 0xffffffff
+MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
+MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+#define MONOLITHIC (1)
+#define PACKETIZED (10)
+static struct _rbu_data {
+        void *image_update_buffer;
+        unsigned long image_update_buffer_size;
+        unsigned long bios_image_size;
+        unsigned long image_update_order_number;
+        spinlock_t lock;
+        unsigned long packet_read_count;
+        unsigned long packet_write_count;
+        unsigned long num_packets;
+        unsigned long packetsize;
+        char mono_image[256];
+        char packet_image[256];
+} rbu_data;
+
+struct packet_data{
+	struct list_head list;
+	size_t length;
+	void *data;
+	int ordernum;
+};
+
+static struct packet_data packet_data_head;
+
+
+static void init_packet_head(void)
+{
+	INIT_LIST_HEAD(&packet_data_head.list);
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+struct rbu_download_device {
+	int type;
+	struct kobject kobj;
+};
+
+static struct rbu_download_device *rbu_download_mono;
+
+static struct rbu_download_device *rbu_download_packet;
+
+static struct device rbu_device;
+
+static int fill_last_packet(void *data, size_t length)
+{
+        struct list_head *ptemp_list;
+        struct packet_data *ppacket = NULL;
+        int packet_count = 0;
+
+        pr_debug("fill_last_packet: entry \n");
+
+        /* check if we have any packets */
+        if (0 == rbu_data.num_packets) {
+                pr_debug("fill_last_packet: num_packets=0\n");
+                return -ENOMEM;
+        }
+
+        packet_count = rbu_data.num_packets;
+
+        ptemp_list = (&packet_data_head.list)->next;
+
+        while(--packet_count) {
+                ptemp_list = ptemp_list->next;
+        }
+
+        ppacket = list_entry(ptemp_list,struct packet_data, list);
+
+        if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
+                printk(KERN_WARNING "fill_last_packet: packet size data "
+                                "overrun\n");
+                return -ENOMEM;
+        }
+
+        pr_debug("fill_last_packet : buffer = %p\n", ppacket->data);
+
+        /* copy the incoming data in to the new buffer */
+        memcpy((ppacket->data + rbu_data.packet_write_count),
+                data, length);
+
+        if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
+                /*
+                 this was the last data chunk in the packet
+                 so reinitialize the packet data counter to zero
+                */
+                rbu_data.packet_write_count = 0;
+        } else {
+                rbu_data.packet_write_count += length;
+        }
+        pr_debug("fill_last_packet: exit \n");
+        return 0;
+}
+
+/*
+ get_free_pages_limited:
+ This is a helper function which allocates free pages based on an upper limit.
+ On x86_64 or 64 bit arch the memory allocation goes above 4GB space which is
+ not addressable by the BIOS. This function tries to get allocation below the
+ limit (4GB) address. It first tries to allocate memory normally using the
+ GFP_KERNEL argument if the incoming limit is non-zero and if the returned
+ physical memory address exceeds the upper limit, the allocated pages are freed
+ and the memory is reallocated using the GFP_DMA argument.
+*/
+static void *get_free_pages_limited(unsigned long size,
+                                    int *ordernum,
+                                    unsigned long limit)
+{
+        unsigned long img_buf_phys_addr;
+        void *pbuf = NULL;
+
+        *ordernum = get_order(size);
+        /*
+         Check if we are not getting a very large file.
+         This can happen as a user error in entering the file size
+        */
+        if (*ordernum == BITS_PER_LONG) {
+                pr_debug("get_free_pages_limited: Incoming size is"
+                        " very large\n");
+                return NULL;
+        }
+
+        /* try allocating a new buffer to fit the request */
+        pbuf =(unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);
+
+        if (pbuf != NULL) {
+                /* check if the image is with in limits */
+                img_buf_phys_addr = (unsigned long)virt_to_phys(pbuf);
+
+                if ((limit != 0) && ((img_buf_phys_addr + size) > limit)) {
+                        pr_debug("Got memory above 4GB range, free this "
+                                "and try with DMA memory\n");
+                        /* free this memory as we need it with in 4GB range */
+                        free_pages ((unsigned long)pbuf, *ordernum);
+                        /*
+                         Try allocating a new buffer from the GFP_DMA range
+                         as it is with in 16MB range.
+                        */
+                        pbuf =(unsigned char *)__get_free_pages(GFP_DMA,
+                                                *ordernum);
+                        if (pbuf == NULL)
+                                pr_debug("Failed to get memory of size %ld "
+                                        "using GFP_DMA\n", size);
+                }
+        }
+        return pbuf;
+}
+
+static int create_packet(size_t length)
+{
+        struct packet_data *newpacket;
+        int ordernum = 0;
+
+        pr_debug("create_packet: entry \n");
+
+        if (rbu_data.packetsize == 0 ) {
+                pr_debug("create_packet: packetsize not specified\n");
+                return -EINVAL;
+        }
+
+        newpacket = kmalloc(sizeof(struct packet_data) ,GFP_KERNEL);
+        if(newpacket == NULL) {
+                printk(KERN_WARNING "create_packet: failed to allocate new "
+                        "packet\n");
+                return -ENOMEM;
+        }
+
+        /* there is no upper limit on memory address for packetized mechanism*/
+        newpacket->data = get_free_pages_limited(rbu_data.packetsize,
+                                &ordernum, 0);
+        pr_debug("create_packet: newpacket %p\n", newpacket->data);
+
+        if(newpacket->data == NULL) {
+                printk(KERN_WARNING "create_packet: failed to allocate new "
+                        "packet\n");
+                kfree(newpacket);
+                return -ENOMEM;
+        }
+
+        newpacket->ordernum = ordernum;
+        ++rbu_data.num_packets;
+        /* initialize the newly created packet headers */
+        INIT_LIST_HEAD(&newpacket->list);
+        list_add_tail(&newpacket->list, &packet_data_head.list);
+        /* packets have fixed size*/
+        newpacket->length = rbu_data.packetsize;
+
+        pr_debug("create_packet: exit \n");
+
+        return 0;
+}
+
+static int packetize_data(void *data, size_t length)
+{
+        int rc = 0;
+
+        if (rbu_data.packet_write_count == 0) {
+                if ((rc = create_packet(length)) != 0 )
+                        return rc;
+        }
+        /* fill data in to the packet */
+        if ((rc = fill_last_packet(data, length)) != 0)
+                return rc;
+
+        return rc;
+}
+
+/*
+ do_packet_read :
+ This is a helper function which reads the packet data of the
+ current list.
+ data: is the incoming buffer
+ ptemp_list: points to the incoming list item
+ length: is the length of the free space in the buffer.
+ bytes_read: is the total number of bytes read already from
+ the packet list
+ list_read_count: is the counter to keep track of the number
+ of bytes read out of each packet.
+*/
+int do_packet_read(char *data,
+                   struct list_head *ptemp_list,
+                   int length,
+                   int bytes_read,
+                   int *list_read_count)
+{
+        void *ptemp_buf;
+        struct packet_data *newpacket = NULL;
+        int bytes_copied = 0;
+        int j = 0;
+
+        newpacket = list_entry(ptemp_list,struct packet_data, list);
+        *list_read_count += newpacket->length;
+
+        if (*list_read_count > bytes_read) {
+                /* point to the start of unread data */
+                j = newpacket->length - (*list_read_count - bytes_read);
+                /* point to the offset in the packet buffer*/
+                ptemp_buf = (u8 *)newpacket->data + j;
+                /* check if there is enough room in the incoming buffer*/
+                if (length > (*list_read_count - bytes_read))
+                        /* copy what ever is there in this packet and move on*/
+                        bytes_copied = (*list_read_count - bytes_read);
+                else
+                        /* copy the remaining */
+                        bytes_copied = length;
+                memcpy(data, ptemp_buf, bytes_copied);
+        }
+        return bytes_copied;
+}
+
+static void packet_empty_list(void)
+{
+        struct list_head *ptemp_list;
+        struct list_head *pnext_list;
+        struct packet_data *newpacket;
+
+        ptemp_list = (&packet_data_head.list)->next;
+        while(!list_empty(ptemp_list)) {
+                newpacket = list_entry(ptemp_list, struct packet_data, list);
+                pnext_list = ptemp_list->next;
+                list_del(ptemp_list);
+                ptemp_list = pnext_list;
+                /*
+                 zero out the RBU packet memory before freeing to make sure
+                 there are no stale RBU packets left in memory
+                */
+                memset(newpacket->data, 0, rbu_data.packetsize);
+                free_pages((unsigned long)newpacket->data, newpacket->ordernum);
+                kfree(newpacket);
+        }
+        rbu_data.packet_write_count = 0;
+        rbu_data.packet_read_count = 0;
+        rbu_data.num_packets = 0;
+}
+
+/*
+ img_update_free:
+ Frees the buffer allocated for storing BIOS image
+ Always called with lock held and returned with lock held
+*/
+static void img_update_free( void)
+{
+        if (rbu_data.image_update_buffer == NULL)
+                return;
+
+        /*
+         zero out this buffer before freeing it to get rid of any stale
+         BIOS image copied in memory.
+        */
+        memset(rbu_data.image_update_buffer, 0,
+                rbu_data.image_update_buffer_size);
+        free_pages((unsigned long)rbu_data.image_update_buffer,
+                rbu_data.image_update_order_number);
+        /* Re-initialize the rbu_data variables after a free */
+        rbu_data.image_update_buffer = NULL;
+        rbu_data.image_update_buffer_size = 0;
+        rbu_data.bios_image_size = 0;
+}
+
+/*
+ img_update_realloc:
+ This function allocates the contiguous pages to accomodate the requested
+ size of data. The memory address and size values are stored globally and
+ on every call to this function the new size is checked to see if more
+ data is required than the existing size. If true the previous memory is freed
+ and new allocation is done to accomodate the new size. If the incoming size is
+ less then than the already allocated size, then that  memory is reused.
+ This function is called with lock held and returna with lock held.
+*/
+static int img_update_realloc(unsigned long size)
+{
+        unsigned char *image_update_buffer = NULL;
+        unsigned long rc;
+        int ordernum =0;
+
+        /* check if the buffer of sufficient size has been already allocated */
+	if (rbu_data.image_update_buffer_size >= size) {
+        	/* check for corruption */
+                if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {
+                        printk(KERN_ERR "img_update_realloc: corruption check "
+                                "failed\n");
+                        return -EINVAL;
+                }
+                /* we have a valid pre-allocated buffer with sufficient size */
+                return 0;
+    	}
+
+        /* free any previously allocated buffer */
+        img_update_free();
+
+        /*
+         This has already been called as locked so we can now unlock
+         and proceed to calling get_free_pages_limited as this function
+         can sleep
+        */
+        spin_unlock(&rbu_data.lock);
+
+        image_update_buffer = (unsigned char *)get_free_pages_limited(size,
+                &ordernum,
+                BIOS_SCAN_LIMIT);
+
+        /* acquire the spinlock again */
+        spin_lock(&rbu_data.lock);
+
+        if (image_update_buffer != NULL) {
+                rbu_data.image_update_buffer = image_update_buffer;
+                rbu_data.image_update_buffer_size = PAGE_SIZE << ordernum;
+                rbu_data.image_update_order_number = ordernum;
+                memset(rbu_data.image_update_buffer,0,
+                        rbu_data.image_update_buffer_size);
+                rc = 0;
+        } else {
+                pr_debug("Not enough memory for image update:order number = %d"
+                        ",size = %ld\n",ordernum, size);
+                rc = -ENOMEM;
+        }
+
+        return rc;
+} /* img_update_realloc */
+
+static ssize_t dummy_store (struct rbu_download_device *rbu_dev,
+                     const char *buf, size_t count, int type)
+{
+	/* do nothing */
+	return count; 
+}
+
+static ssize_t rbu_show_image_size (struct rbu_download_device *rbu_dev, 
+				    char *buf, int type)
+{
+        unsigned int size = 0;
+	if ( type == MONOLITHIC )
+		size = sprintf(buf, "%lu\n",  rbu_data.bios_image_size);
+	else
+		size = sprintf(buf, "%lu\n", rbu_data.packetsize);
+        return size;
+}
+
+static ssize_t rbu_show_image_name (struct rbu_download_device *rbu_dev, 
+				    char *buf, int type)
+{
+        unsigned int size = 0;
+        char *image;
+        
+	if (type == MONOLITHIC ) 
+		image = rbu_data.mono_image;
+	else
+		image = rbu_data.packet_image;
+
+	size = sprintf(buf, "%s\n", image);
+        return size;
+}
+
+static ssize_t rbu_store_image_name (struct rbu_download_device *rbu_dev, 
+				     const char *buf, size_t count, int type)
+{
+        int rc = count;
+        const struct firmware *fw_entry;
+	char *image_name = NULL;
+
+        spin_lock(&rbu_data.lock);
+
+        if (strlen(buf) < 256 ) {
+		if (type == MONOLITHIC ){
+                	sscanf(buf, "%s",rbu_data.mono_image);
+			image_name = rbu_data.mono_image;
+		} else {
+                	sscanf(buf, "%s",rbu_data.packet_image);
+			image_name = rbu_data.packet_image;
+		}
+
+		spin_unlock(&rbu_data.lock);	
+
+                rc = request_firmware(&fw_entry, image_name,
+                                &rbu_device);
+		if (rc) {
+                        printk(KERN_ERR "rbu_store_image_name: " 
+				"Firmware not available %d\n", rc);
+			return rc;
+		}
+			
+		pr_debug("rbu_store_image_name: "
+			"request_firmware is successful  "
+			"fw->size = %lu\n", fw_entry->size);
+
+		spin_lock(&rbu_data.lock);
+		if (type == MONOLITHIC ){
+			rbu_data.bios_image_size = fw_entry->size;
+			rc = img_update_realloc(fw_entry->size);
+			if (rc == 0) {
+				memcpy(rbu_data.image_update_buffer, 
+					fw_entry->data, fw_entry->size);
+				rc = count;
+			}
+		} else {
+			/* if a new packet is entered free all previsou 
+			   packets and start over.
+			 */
+			if ( rbu_data.packetsize != fw_entry->size )
+				packet_empty_list();
+
+			rbu_data.packetsize = fw_entry->size;
+			rc = packetize_data(fw_entry->data, fw_entry->size);
+			if ( rc == 0 )
+				rc = count;
+		}
+		spin_unlock(&rbu_data.lock);
+		release_firmware(fw_entry);
+        } else {
+		spin_unlock(&rbu_data.lock);
+		rc = -ENOMEM;
+	}
+
+        return rc;
+}
+
+/* no default attributes yet. */
+static struct attribute * def_attrs[] = { NULL, };
+
+struct rbu_attribute {
+	struct attribute attr;
+	ssize_t (*show) (struct rbu_download_device *rbu_dev,
+		char *buf, int type);
+	ssize_t (*store)(struct rbu_download_device *rbu_dev, 
+		const char *buf , size_t count, int type);
+	int type;
+};
+
+#define RBU_DEVICE_ATTR(_name,_mode,_show,_store, _type) \
+struct rbu_attribute rbu_attr_##_name = {       \
+	.attr ={.name= __stringify(_name), .mode= _mode, .owner= THIS_MODULE},\
+	.show = _show,                                \
+	.store = _store,                                \
+	.type = _type,	 \
+};
+
+#define to_rbu_attr(_attr) container_of(_attr,struct rbu_attribute,attr)
+#define to_rbu_download_device(obj)  \
+		container_of(obj,struct rbu_download_device,kobj)
+
+
+static RBU_DEVICE_ATTR(mono_name, 0644, rbu_show_image_name, 
+				rbu_store_image_name, MONOLITHIC);
+
+static RBU_DEVICE_ATTR(mono_size, 0444, rbu_show_image_size, 
+				dummy_store, MONOLITHIC);
+
+static RBU_DEVICE_ATTR(packet_name, 0644, rbu_show_image_name,
+                                rbu_store_image_name, PACKETIZED);
+
+static RBU_DEVICE_ATTR(packet_size, 0444, rbu_show_image_size,
+                                dummy_store, PACKETIZED);
+
+static  ssize_t rbu_attr_store (struct kobject *kobj,
+				struct attribute *attr,
+				const char *buf, 
+				size_t count) 
+{
+	struct rbu_download_device *rbu_dev = to_rbu_download_device(kobj);
+	struct rbu_attribute *rbu_attr = to_rbu_attr(attr);
+	ssize_t rc = count;
+
+	pr_debug("rbu_attr_store: entry type = %d\n", rbu_attr->type);
+	
+	if (rbu_attr->store)
+		rc = rbu_attr->store(rbu_dev, buf, count, rbu_attr->type);
+
+	return rc;
+}
+
+static ssize_t rbu_attr_show (struct kobject * kobj, 
+			      struct attribute *attr, 
+			      char *buf)
+{
+	struct rbu_download_device *rbu_dev = to_rbu_download_device(kobj);
+	struct rbu_attribute *rbu_attr = to_rbu_attr(attr);
+	ssize_t rc = 0;
+
+	pr_debug("rbu_attr_show: entry type = %d\n", rbu_attr->type);
+
+	if (rbu_attr->show)
+		rc = rbu_attr->show(rbu_dev, buf, rbu_attr->type);
+	return rc;
+}
+
+static struct sysfs_ops rbu_attr_ops = { 
+	.show = rbu_attr_show,
+	.store = rbu_attr_store,
+};
+
+static struct kobj_type ktype_dell_rbu = { 
+	.sysfs_ops	= &rbu_attr_ops,
+	.default_attrs	= def_attrs,
+};
+static decl_subsys(dell_rbu,&ktype_dell_rbu,NULL);
+
+static int rbu_download_device_register(struct rbu_download_device *rbu_dev, 
+					int type)
+{
+        int rc = 0;
+        if (!rbu_dev)
+                return 1;
+        memset(rbu_dev, 0, sizeof (*rbu_dev));
+	if (type == MONOLITHIC)
+        	kobject_set_name(&rbu_dev->kobj, "monolithic");
+	else 
+        	kobject_set_name(&rbu_dev->kobj, "packetized");
+        kobj_set_kset_s(rbu_dev,dell_rbu_subsys);
+        rc = kobject_register(&rbu_dev->kobj);
+        if (!rc) {
+		if (type == MONOLITHIC ) {
+			sysfs_create_file(&rbu_dev->kobj,
+				&rbu_attr_mono_name.attr);
+			sysfs_create_file(&rbu_dev->kobj,
+				&rbu_attr_mono_size.attr);
+		} else {
+			sysfs_create_file(&rbu_dev->kobj,
+				&rbu_attr_packet_name.attr);
+			sysfs_create_file(&rbu_dev->kobj,
+				&rbu_attr_packet_size.attr);
+		}
+	} else 
+		pr_debug("rbu_download_device_register: "
+			"kobject_register %d \n", rc);
+	pr_debug("rbu_download_device_register: rbu_dev addr %p\n", rbu_dev);
+        return rc;
+}
+
+static struct rbu_download_device *create_rbu_download_entry(int type) 
+{
+	struct rbu_download_device *rbu_dev = NULL;
+	rbu_dev = kmalloc( sizeof(struct rbu_download_device), 
+		GFP_KERNEL);
+	if (!rbu_dev) {
+		printk(KERN_ERR "create_rbu_download_entry: kmalloc failed\n");
+		return NULL;
+	}
+	rbu_dev->type = type;
+        if (rbu_download_device_register(rbu_dev, type))  {
+		pr_debug("create_rbu_download_entry: "
+			"rbu_download_device_register failed \n");
+		kfree(rbu_dev);
+        }
+
+	pr_debug("create_rbu_download_entry: rbu_dev %p\n", rbu_dev);	
+	return rbu_dev;
+}
+
+static void remove_rbu_download_entry(struct rbu_download_device *rbu_dev, 
+				       int type) 
+{
+	pr_debug("remove_rbu_download_entry: rbu_dev ptr %p \n", rbu_dev);	
+	if (rbu_dev != NULL) {
+		kobject_unregister(&rbu_dev->kobj);
+		kfree(rbu_dev);
+	}
+	
+}
+
+static int __init dcdrbu_init(void)
+{
+        int rc = 0;
+	spin_lock_init(&rbu_data.lock);
+	
+	init_packet_head();
+	
+        device_initialize(&rbu_device);
+
+	rc = firmware_register(&dell_rbu_subsys);
+        if (rc < 0) {
+                printk(KERN_WARNING "dcdrbu_init: firmware_register"
+			" dell_rbu failed\n");
+                return rc;
+        }
+	
+	rbu_download_mono = create_rbu_download_entry (MONOLITHIC);
+	if (rbu_download_mono == NULL) {	
+		firmware_unregister(&dell_rbu_subsys);
+		return -ENOMEM;
+	}
+	
+	rbu_download_packet=  create_rbu_download_entry (PACKETIZED);
+	if (rbu_download_packet == NULL) {	
+		remove_rbu_download_entry(rbu_download_mono, MONOLITHIC);
+		firmware_unregister(&dell_rbu_subsys);
+		return -ENOMEM;
+	}
+        strncpy(rbu_device.bus_id,"firmware", BUS_ID_SIZE);
+        return rc;
+}
+
+static __exit void dcdrbu_exit( void)
+{
+	spin_lock(&rbu_data.lock);
+        packet_empty_list();
+        img_update_free();
+        spin_unlock(&rbu_data.lock);
+	remove_rbu_download_entry(rbu_download_packet, PACKETIZED);
+	remove_rbu_download_entry(rbu_download_mono, MONOLITHIC);
+        firmware_unregister(&dell_rbu_subsys);
+}
+
+module_exit(dcdrbu_exit);
+module_init(dcdrbu_init);
+
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Kconfig linux-2.6.11.8/drivers/firmware/Kconfig
--- linux-2.6.11.8.ORIG/drivers/firmware/Kconfig	2005-05-13 12:07:58.000000000 -0500
+++ linux-2.6.11.8/drivers/firmware/Kconfig	2005-05-13 12:07:00.000000000 -0500
@@ -58,4 +58,16 @@ config EFI_PCDP
 
 	  See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf>
 
+config DELL_RBU
+        tristate "BIOS update support for DELL systems via sysfs"
+        default n
+        help
+          Say Y if you want to have the option of updating the BIOS for your
+	  DELL system. Note you need a supporting application to comunicate 
+	  with the BIOS regardign the new image for the image update to 
+	  take effect.
+
+	  See <file:Documentation/DELL_RBU.txt> for more details on the driver.
+
+
 endmenu
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Makefile linux-2.6.11.8/drivers/firmware/Makefile
--- linux-2.6.11.8.ORIG/drivers/firmware/Makefile	2005-05-13 12:08:12.000000000 -0500
+++ linux-2.6.11.8/drivers/firmware/Makefile	2005-05-09 15:15:16.000000000 -0500
@@ -4,3 +4,4 @@
 obj-$(CONFIG_EDD)             	+= edd.o
 obj-$(CONFIG_EFI_VARS)		+= efivars.o
 obj-$(CONFIG_EFI_PCDP)		+= pcdp.o
+obj-$(CONFIG_DELL_RBU)		+= dell_rbu.o

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-26 20:37 ` Greg KH
@ 2005-05-26 21:36   ` Marcel Holtmann
  0 siblings, 0 replies; 28+ messages in thread
From: Marcel Holtmann @ 2005-05-26 21:36 UTC (permalink / raw)
  To: Greg KH; +Cc: Abhay_Salunke, linux-kernel, ranty

Hi Abhay,

> > static int __init dcdrbu_init(void)
> > {
> >         int rc = 0;
> >         const struct firmware *fw;
> > 
> >         device_initialize(&rbu_device_type);
> >         device_initialize(&rbu_device);
> > 
> >         strncpy(rbu_device.bus_id,"dell_rbu.bin", BUS_ID_SIZE);
> >         strncpy(rbu_device_type.bus_id,"dell_rbu1.bin", BUS_ID_SIZE);
> > 
> >         rc = request_firmware(&fw, "dell_rbu_type", &rbu_device_type);
> 
> Try registering the device with sysfs first.

and then you use the same device for both calls and put the firmware
names into the request_firmware() calls. This is the filename you are
going to request from userspace.

Regards

Marcel



^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-26 16:37 Abhay_Salunke
  2005-05-26 16:56 ` Matt Domsch
@ 2005-05-26 20:37 ` Greg KH
  2005-05-26 21:36   ` Marcel Holtmann
  1 sibling, 1 reply; 28+ messages in thread
From: Greg KH @ 2005-05-26 20:37 UTC (permalink / raw)
  To: Abhay_Salunke; +Cc: linux-kernel, ranty

On Thu, May 26, 2005 at 11:37:44AM -0500, Abhay_Salunke@Dell.com wrote:
> > -----Original Message-----
> > From: Greg KH [mailto:greg@kroah.com]
> > Sent: Monday, May 23, 2005 10:48 AM
> > To: Salunke, Abhay
> > Cc: linux-kernel@vger.kernel.org; akpm@osdl.org; Domsch, Matt
> > Subject: Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new
> Dell
> > BIOS update driver
> > 
> > On Mon, May 23, 2005 at 09:52:05AM -0500, Abhay_Salunke@Dell.com
> wrote:
> > > Greg,
> > > >
> > > > Also, what's wrong with using the existing firmware interface in
> the
> > > > kernel?
> > > request_firmware requires the $FIRMWARE env to be populated with the
> > > firmware image name or the firmware image name needs to be hardcoded
> > > within  the call to request_firmware. Since the user is free to
> change
> > > the BIOS update image at will, it may not be possible if we use
> > > $FIRMWARE also I am not sure if this env variable might be
> conflicting
> > > to some other driver.
> > 
> > As others have already stated, this doesn't really matter.  Make it
> > "dell_bios_update", if any device names their firmware that, well,
> > that's their problem...
> 
> OK, I have been trying to use request_firmware but it always fails with
> return code -2. This is the code snippet below, any thoughts?
> 
> static struct device rbu_device_type;
> 
> static struct device rbu_device;

Struct devices must be created with kmalloc.

> static int __init dcdrbu_init(void)
> {
>         int rc = 0;
>         const struct firmware *fw;
> 
>         device_initialize(&rbu_device_type);
>         device_initialize(&rbu_device);
> 
>         strncpy(rbu_device.bus_id,"dell_rbu.bin", BUS_ID_SIZE);
>         strncpy(rbu_device_type.bus_id,"dell_rbu1.bin", BUS_ID_SIZE);
> 
>         rc = request_firmware(&fw, "dell_rbu_type", &rbu_device_type);

Try registering the device with sysfs first.

Good luck,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-05-26 18:43 Abhay_Salunke
  0 siblings, 0 replies; 28+ messages in thread
From: Abhay_Salunke @ 2005-05-26 18:43 UTC (permalink / raw)
  To: Matt_Domsch; +Cc: greg, linux-kernel, ranty

Matt,
I may be wrong; but I think request_firmware(&fw, NAME , device)  will
create an entry /sys/firmware/NAME/(loading,data).
You can simply download the firmware by doing 
echo 1 > /sys/firmware/NAME/loading
cat rbu.image > /sys/firmware/NAME/data.
After this is done request_firmware returns and the image can be
actually 
copied to the device using fw->data and fw-size. 
Once this is complete the fw can be released.
Not sure why do I need a firmware image file in /lib/firmware?
Thanks,
Abhay

> -----Original Message-----
> From: Domsch, Matt
> Sent: Thursday, May 26, 2005 11:56 AM
> To: Salunke, Abhay
> Cc: greg@kroah.com; linux-kernel@vger.kernel.org; ranty@debian.org
> Subject: Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new
Dell
> BIOS update driver
> 
> On Thu, May 26, 2005 at 11:37:44AM -0500, Abhay_Salunke@Dell.com
wrote:
> > > -----Original Message-----
> > > From: Greg KH [mailto:greg@kroah.com]
> > > Sent: Monday, May 23, 2005 10:48 AM
> > > To: Salunke, Abhay
> > > Cc: linux-kernel@vger.kernel.org; akpm@osdl.org; Domsch, Matt
> > > Subject: Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for
new
> > Dell
> > > BIOS update driver
> > >
> > > On Mon, May 23, 2005 at 09:52:05AM -0500, Abhay_Salunke@Dell.com
> > wrote:
> > > > Greg,
> > > > >
> > > > > Also, what's wrong with using the existing firmware interface
in
> > the
> > > > > kernel?
> > > > request_firmware requires the $FIRMWARE env to be populated with
the
> > > > firmware image name or the firmware image name needs to be
hardcoded
> > > > within  the call to request_firmware. Since the user is free to
> > change
> > > > the BIOS update image at will, it may not be possible if we use
> > > > $FIRMWARE also I am not sure if this env variable might be
> > conflicting
> > > > to some other driver.
> > >
> > > As others have already stated, this doesn't really matter.  Make
it
> > > "dell_bios_update", if any device names their firmware that, well,
> > > that's their problem...
> >
> > OK, I have been trying to use request_firmware but it always fails
with
> > return code -2. This is the code snippet below, any thoughts?
> 
> -2 is -ENOENT, "No such file or directory".
> It's looking for a file called /lib/firmware/dell_rbu_type, and not
> finding it.  That probably isn't the name of the file you want it to
> look for.
> 
> Thanks,
> Matt
> 
> --
> Matt Domsch
> Software Architect
> Dell Linux Solutions linux.dell.com & www.dell.com/linux
> Linux on Dell mailing lists @ http://lists.us.dell.com


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-26 16:37 Abhay_Salunke
@ 2005-05-26 16:56 ` Matt Domsch
  2005-05-26 20:37 ` Greg KH
  1 sibling, 0 replies; 28+ messages in thread
From: Matt Domsch @ 2005-05-26 16:56 UTC (permalink / raw)
  To: Abhay_Salunke; +Cc: greg, linux-kernel, ranty

On Thu, May 26, 2005 at 11:37:44AM -0500, Abhay_Salunke@Dell.com wrote:
> > -----Original Message-----
> > From: Greg KH [mailto:greg@kroah.com]
> > Sent: Monday, May 23, 2005 10:48 AM
> > To: Salunke, Abhay
> > Cc: linux-kernel@vger.kernel.org; akpm@osdl.org; Domsch, Matt
> > Subject: Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new
> Dell
> > BIOS update driver
> > 
> > On Mon, May 23, 2005 at 09:52:05AM -0500, Abhay_Salunke@Dell.com
> wrote:
> > > Greg,
> > > >
> > > > Also, what's wrong with using the existing firmware interface in
> the
> > > > kernel?
> > > request_firmware requires the $FIRMWARE env to be populated with the
> > > firmware image name or the firmware image name needs to be hardcoded
> > > within  the call to request_firmware. Since the user is free to
> change
> > > the BIOS update image at will, it may not be possible if we use
> > > $FIRMWARE also I am not sure if this env variable might be
> conflicting
> > > to some other driver.
> > 
> > As others have already stated, this doesn't really matter.  Make it
> > "dell_bios_update", if any device names their firmware that, well,
> > that's their problem...
> 
> OK, I have been trying to use request_firmware but it always fails with
> return code -2. This is the code snippet below, any thoughts?

-2 is -ENOENT, "No such file or directory".
It's looking for a file called /lib/firmware/dell_rbu_type, and not
finding it.  That probably isn't the name of the file you want it to
look for.

Thanks,
Matt

-- 
Matt Domsch
Software Architect
Dell Linux Solutions linux.dell.com & www.dell.com/linux
Linux on Dell mailing lists @ http://lists.us.dell.com

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-05-26 16:37 Abhay_Salunke
  2005-05-26 16:56 ` Matt Domsch
  2005-05-26 20:37 ` Greg KH
  0 siblings, 2 replies; 28+ messages in thread
From: Abhay_Salunke @ 2005-05-26 16:37 UTC (permalink / raw)
  To: greg; +Cc: linux-kernel, ranty

> -----Original Message-----
> From: Greg KH [mailto:greg@kroah.com]
> Sent: Monday, May 23, 2005 10:48 AM
> To: Salunke, Abhay
> Cc: linux-kernel@vger.kernel.org; akpm@osdl.org; Domsch, Matt
> Subject: Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new
Dell
> BIOS update driver
> 
> On Mon, May 23, 2005 at 09:52:05AM -0500, Abhay_Salunke@Dell.com
wrote:
> > Greg,
> > >
> > > Also, what's wrong with using the existing firmware interface in
the
> > > kernel?
> > request_firmware requires the $FIRMWARE env to be populated with the
> > firmware image name or the firmware image name needs to be hardcoded
> > within  the call to request_firmware. Since the user is free to
change
> > the BIOS update image at will, it may not be possible if we use
> > $FIRMWARE also I am not sure if this env variable might be
conflicting
> > to some other driver.
> 
> As others have already stated, this doesn't really matter.  Make it
> "dell_bios_update", if any device names their firmware that, well,
> that's their problem...

OK, I have been trying to use request_firmware but it always fails with
return code -2. This is the code snippet below, any thoughts?

static struct device rbu_device_type;

static struct device rbu_device;

static int __init dcdrbu_init(void)
{
        int rc = 0;
        const struct firmware *fw;

        device_initialize(&rbu_device_type);
        device_initialize(&rbu_device);

        strncpy(rbu_device.bus_id,"dell_rbu.bin", BUS_ID_SIZE);
        strncpy(rbu_device_type.bus_id,"dell_rbu1.bin", BUS_ID_SIZE);

        rc = request_firmware(&fw, "dell_rbu_type", &rbu_device_type);

        if (rc) {
                printk(KERN_ERR "dcdrbu_init: Firmware 1 missing "
                        "%d\n", rc);
                return -EIO;
        }

        release_firmware(fw);

        rc = request_firmware(&fw, "dell_rbu_data", &rbu_device);
        if (rc) {
                printk(KERN_ERR "dcdrbu_init: Firmware 2 missing "
                        "%d\n", rc);
                return -EIO;
        }

        release_firmware(fw);

        return rc;
}

static __exit void dcdrbu_exit( void)
{
}

module_exit(dcdrbu_exit);
module_init(dcdrbu_init);

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-23 14:52 Abhay_Salunke
  2005-05-23 14:58 ` Arjan van de Ven
  2005-05-23 14:59 ` Marcel Holtmann
@ 2005-05-23 15:48 ` Greg KH
  2 siblings, 0 replies; 28+ messages in thread
From: Greg KH @ 2005-05-23 15:48 UTC (permalink / raw)
  To: Abhay_Salunke; +Cc: linux-kernel, akpm, Matt_Domsch

On Mon, May 23, 2005 at 09:52:05AM -0500, Abhay_Salunke@Dell.com wrote:
> Greg,
> > 
> > Also, what's wrong with using the existing firmware interface in the
> > kernel?
> request_firmware requires the $FIRMWARE env to be populated with the
> firmware image name or the firmware image name needs to be hardcoded
> within  the call to request_firmware. Since the user is free to change
> the BIOS update image at will, it may not be possible if we use
> $FIRMWARE also I am not sure if this env variable might be conflicting
> to some other driver.

As others have already stated, this doesn't really matter.  Make it
"dell_bios_update", if any device names their firmware that, well,
that's their problem...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-23 14:52 Abhay_Salunke
  2005-05-23 14:58 ` Arjan van de Ven
@ 2005-05-23 14:59 ` Marcel Holtmann
  2005-05-23 15:48 ` Greg KH
  2 siblings, 0 replies; 28+ messages in thread
From: Marcel Holtmann @ 2005-05-23 14:59 UTC (permalink / raw)
  To: Abhay_Salunke; +Cc: greg, linux-kernel, akpm, Matt_Domsch

Hi Abhay,

> > Also, what's wrong with using the existing firmware interface in the
> > kernel?
> request_firmware requires the $FIRMWARE env to be populated with the
> firmware image name or the firmware image name needs to be hardcoded
> within  the call to request_firmware.

the latter one. Don't mess with the $FIRMWARE env, because this comes
from the kernel hotplug call.

> Since the user is free to change
> the BIOS update image at will, it may not be possible if we use
> $FIRMWARE also I am not sure if this env variable might be conflicting
> to some other driver.

I am not quite sure what's the problem here. Tell the kernel what
firmware image to request. Something like

	echo "firmware-filename" > /sys/firmware/dell_rbu/download

Regards

Marcel



^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-23 14:52 Abhay_Salunke
@ 2005-05-23 14:58 ` Arjan van de Ven
  2005-05-23 14:59 ` Marcel Holtmann
  2005-05-23 15:48 ` Greg KH
  2 siblings, 0 replies; 28+ messages in thread
From: Arjan van de Ven @ 2005-05-23 14:58 UTC (permalink / raw)
  To: Abhay_Salunke; +Cc: greg, linux-kernel, akpm, Matt_Domsch

On Mon, 2005-05-23 at 09:52 -0500, Abhay_Salunke@Dell.com wrote:
> Greg,
> > 
> > Also, what's wrong with using the existing firmware interface in the
> > kernel?
> request_firmware requires the $FIRMWARE env to be populated with the
> firmware image name or the firmware image name needs to be hardcoded
> within  the call to request_firmware

ok so far

> . Since the user is free to change
> the BIOS update image at will, it may not be possible if we use
> $FIRMWARE

it is, just have a sysfs file to let the user echo the firmware name
in , with a suitable default if he doesn't.



^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-05-23 14:52 Abhay_Salunke
  2005-05-23 14:58 ` Arjan van de Ven
                   ` (2 more replies)
  0 siblings, 3 replies; 28+ messages in thread
From: Abhay_Salunke @ 2005-05-23 14:52 UTC (permalink / raw)
  To: greg; +Cc: linux-kernel, akpm, Matt_Domsch

Greg,
> 
> Also, what's wrong with using the existing firmware interface in the
> kernel?
request_firmware requires the $FIRMWARE env to be populated with the
firmware image name or the firmware image name needs to be hardcoded
within  the call to request_firmware. Since the user is free to change
the BIOS update image at will, it may not be possible if we use
$FIRMWARE also I am not sure if this env variable might be conflicting
to some other driver.

Thanks
Abhay

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-19 12:03 Abhay_Salunke
@ 2005-05-19 14:42 ` Greg KH
  0 siblings, 0 replies; 28+ messages in thread
From: Greg KH @ 2005-05-19 14:42 UTC (permalink / raw)
  To: Abhay_Salunke; +Cc: linux-kernel, akpm, Matt_Domsch, adobriyan

On Thu, May 19, 2005 at 07:03:17AM -0500, Abhay_Salunke@Dell.com wrote:
> > On Wed, May 18, 2005 at 01:13:42PM -0500, Abhay Salunke wrote:
> > > This is a resubmit of the patch after incorporating all the inputs
> > > from revieweres. This also has a fix where the packets were leaked in
> > > the function create_packet line#227.
> > 
> > You did not address the issues I had with your use of binary sysfs files
> > for all file types.  Please fix that up.
> > 
> > Also, what's wrong with using the existing firmware interface in the
> > kernel?
> > 
> I am working on it; just wanted to address all the cosmetic issues
> first.

That is not what you stated above, hence my response.  Next time, please
be clearer.

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* RE: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-05-19 12:03 Abhay_Salunke
  2005-05-19 14:42 ` Greg KH
  0 siblings, 1 reply; 28+ messages in thread
From: Abhay_Salunke @ 2005-05-19 12:03 UTC (permalink / raw)
  To: greg; +Cc: linux-kernel, akpm, Matt_Domsch, adobriyan

> -----Original Message-----
> From: Greg KH [mailto:greg@kroah.com]
> Sent: Wednesday, May 18, 2005 10:33 PM
> To: Salunke, Abhay
> Cc: linux-kernel@vger.kernel.org; Andrew Morton; Domsch, Matt; Alexey
> Dobriyan
> Subject: Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new
Dell
> BIOS update driver
> 
> On Wed, May 18, 2005 at 01:13:42PM -0500, Abhay Salunke wrote:
> > This is a resubmit of the patch after incorporating all the inputs
> > from revieweres. This also has a fix where the packets were leaked
in
> > the function create_packet line#227.
> 
> You did not address the issues I had with your use of binary sysfs
files
> for all file types.  Please fix that up.
> 
> Also, what's wrong with using the existing firmware interface in the
> kernel?
> 
I am working on it; just wanted to address all the cosmetic issues
first.

Thanks,
Abhay 


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
  2005-05-18 18:13 Abhay Salunke
@ 2005-05-19  3:32 ` Greg KH
  0 siblings, 0 replies; 28+ messages in thread
From: Greg KH @ 2005-05-19  3:32 UTC (permalink / raw)
  To: Abhay Salunke; +Cc: linux-kernel, Andrew Morton, matt_domsch, Alexey Dobriyan

On Wed, May 18, 2005 at 01:13:42PM -0500, Abhay Salunke wrote:
> This is a resubmit of the patch after incorporating all the inputs
> from revieweres. This also has a fix where the packets were leaked in
> the function create_packet line#227.

You did not address the issues I had with your use of binary sysfs files
for all file types.  Please fix that up.

Also, what's wrong with using the existing firmware interface in the
kernel?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver
@ 2005-05-18 18:13 Abhay Salunke
  2005-05-19  3:32 ` Greg KH
  0 siblings, 1 reply; 28+ messages in thread
From: Abhay Salunke @ 2005-05-18 18:13 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton; +Cc: abhay_salunke, matt_domsch, Alexey Dobriyan

This is a resubmit of the patch after incorporating all the inputs from revieweres. This also has a fix where the packets were leaked in the function create_packet line#227.

By making a contribution to this project, I certify that:
The contribution was created in whole or in part by me and I have the 
right to submit it under the open source license indicated in the file.

Signed-off-by: Abhay Salunke <Abhay_Salunke@dell.com>

Thanks,
Abhay Salunke
Software Engineer.
DELL Inc

diff -uprN linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt linux-2.6.11.8/Documentation/DELL_RBU.txt
--- linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.8/Documentation/DELL_RBU.txt	2005-05-11 13:22:45.000000000 -0500
@@ -0,0 +1,92 @@
+Purpose:
+Demonstrate the usage of the DELL_RBU (DELL Remote BIOS Update) driver
+for updating BIOS images on Dell hardware.
+
+Scope:
+This document discusses the functionality of the DELL_RBU driver. 
+This driver is required by BIOS update applications shipped by DELL for updating
+BIOS on DELL servers and client systems. 
+
+Overview:
+The rbu driver is designed to be running on 2.6 kernel. 
+This driver is one single dell_rbu.c file (approx 800 lines total).
+The BIOS update is done by writing the new BIOS image in to contiguous physical
+memory addressable by the BIOS. The user application indicates the BIOS regarding 
+the update of a fresh image. The BIOS then scans the memory to find the image and 
+it will then update itself. There are basically two different mechanisms for 
+writing the BIOS image in to contiguous memory 
+1> By writing the image to one single shunk of contiguous physical memory.
+2> By writing image in to smaller chunks of contiguous physical memory.
+The update mechanism is determined by the update application based on the 
+particular system type.
+
+Update mechanism using single physical chunk of memory:
+The rbu driver on its load time created the following entries in sysfs
+/sys/firmware/rbu/rbudatasize
+/sys/firmware/rbu/rbudata
+
+Steps to update the BIOS image:
+
+1> Set the incoming BIOS image size in the /sys/firmware/rbudatasize file.
+
+ e.g. echo XXXXXX > /sys/firmware/rbudatasize 
+NOTE: the size specified is always in decimal.
+
+you can also read back the image size by doing
+cat /sys/firmware/rbudatasize
+
+2> Download the BIOS image by copying the image file to /sys/firmware/rbudata 
+file.
+e.g. cat image.hdr > /sys/firmware/rbudata
+
+you can also read back the image using 
+cat /sys/firmware/rbu/rbudata
+This is usually helpful in verifying the image downloaded.
+
+Step#1 results in the driver allocating contiguous physical memory  of the size
+echoed in to rbudatasize. The subsequent writes to rbudata as described in
+step #2 results in the image getting written to the allocated contiguous physical 
+pages. Repeating step #2 will overwrite the previous data in rbudata file.
+
+On a driver unload the allocated memory is freed and the rbudatasize file reads 0.
+The user should not unload the driver after downloading the new BIOS image for 
+if it wants to update BIOS with that image.
+
+The user can overwrite the rbudata file with a new image. The user has to make 
+sure that the new image size is less than or equal to the image size copied to 
+the rbudatasize file. 
+If the new image is grater than the allocated size then only the allocated size
+gets copied the rest will not.
+
+The user can also free the previous BIOS image as follows
+echo 0 > /sys/firmware/rbu/rbudatasize
+
+If the user tries to set the BIOS image size there is a possiblity that the 
+system may not have enough contiguous physical memory for upadtes, thus the 
+image allocation will fail. The user the needs to verify this by reading back 
+the rbudatasize which will be set to 0.
+
+Update using smaller chunks (packets) of contiguous memory:
+The disadvantage of contiguous allocation is that it may not be always possible
+to get that size of contiuguous chunk of avaliable physical pages as in most 
+Linux systems the memory gets fragmented immideately after a reboot.
+The update using smaller chunks fixes this issue; it also requires the BIOS on 
+the system to support this feature; the update application needs to query this 
+with the BIOS on the system before using this technique. 
+
+The appplication breaks the BIOS image in to small packets; before starting the 
+update using this technique, the application sets the packetdatasize as follows
+
+echo XXXXXX > /sys/firmware/packetdatasize
+Any writes to /sys/firmware/packetdata results in allocation of contiguous 
+physical memory of packetdatasize and the data is written to that meomry.
+Writing 0 to packetdatasize results in freeing of all packets.
+Unloading the driver will also result in freeing up of the allocated packets.
+
+NOTE:
+Afte updating the BIOS image the appplication needs to communicate with the BIOS 
+for enabling the update on the next reboot. The application can then choose to 
+reboot the system imideately or not reboot the system and leave up to the user 
+to do a reboot.
+
+
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c linux-2.6.11.8/drivers/firmware/dell_rbu.c
--- linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.8/drivers/firmware/dell_rbu.c	2005-05-18 13:00:28.102529160 -0500
@@ -0,0 +1,853 @@
+/*
+ * dell_rbu.c
+ * Bios Update driver for Dell systems
+ * Author: Dell Inc
+ *	   Abhay Salunke <abhay_salunke@dell.com>
+ *
+ * Copyright (C) 2004 Dell Inc.
+ *
+ * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by creating 
+ * entries in the /sys file systems on Linux 2.6 and higher kernels.
+ * The driver supports two mechanism to update the BIOS namely contiguous and 
+ * packetized. Both these methods still require to have some application to set
+ * the  CMOS bit indicating the BIOS to update itself after a reboot.
+ * 
+ * Contiguous method:
+ * This driver tries to allocates contiguos physical pages large enough 
+ * to accomodate the BIOS image size specified by the user. The user 
+ * supplied BIOS image is then copied in to the allocated contiguous pages.
+ *
+ * Packetized method:
+ * In case of packetized the driver provides entries in the /sys file systems 
+ * as packetdatasize and packetdata. This driver requires an application to 
+ * break the BIOS image in to fixed sized packet chunks and each packet is 
+ * written to the packetdata entry. The packetdatasize needs to be set once 
+ * and is fixed for all the packets.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation
+ *
+ * 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.
+ */
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/firmware.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#define BIOS_SCAN_LIMIT 0xffffffff
+MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
+MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.7");
+
+static struct _rbu_data {
+	void *image_update_buffer;
+	unsigned long image_update_buffer_size;
+	unsigned long bios_image_size;
+	unsigned long image_update_order_number;
+	spinlock_t lock;
+	unsigned long packet_read_count;
+	unsigned long packet_write_count;
+	unsigned long num_packets;
+	unsigned long packetsize;
+} rbu_data;
+
+struct packet_data{
+	struct list_head list;
+	size_t length;
+	void *data;
+	int ordernum;
+};
+
+
+static struct packet_data packet_data_head;
+
+/* no default attributes yet. */
+static struct attribute * def_attrs[] = { NULL, };
+
+/* don't use show and store attribute functions */
+static struct sysfs_ops rbu_attr_ops = { };
+
+static struct kobj_type ktype_rbu = { 
+	.sysfs_ops	= &rbu_attr_ops,
+	.default_attrs	= def_attrs,
+};
+
+static decl_subsys(rbu,&ktype_rbu,NULL);
+
+static void init_packet_head(void)
+{
+	INIT_LIST_HEAD(&packet_data_head.list);
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+	rbu_data.packetsize = 0;
+}
+
+static int fill_last_packet(void *data, size_t length)
+{
+	struct list_head *ptemp_list;
+	struct packet_data *ppacket = NULL;
+	int packet_count = 0;
+	
+	pr_debug("fill_last_packet: entry \n");
+
+	/* check if we have any packets */
+	if (0 == rbu_data.num_packets) {
+		pr_debug("fill_last_packet: num_packets=0\n");
+		return -ENOMEM;
+	}
+
+	packet_count = rbu_data.num_packets;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	
+	while(--packet_count) {
+		ptemp_list = ptemp_list->next;
+	}
+
+	ppacket = list_entry(ptemp_list,struct packet_data, list);
+
+	if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
+		printk(KERN_WARNING "fill_last_packet: packet size data "
+				"overrun\n");
+		return -ENOMEM;
+	}
+	
+	pr_debug("fill_last_packet : buffer = %p\n", ppacket->data);
+
+	/* copy the incoming data in to the new buffer */
+	memcpy((ppacket->data + rbu_data.packet_write_count), 
+		data, length);
+	
+	if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
+		/* 
+		 this was the last data chunk in the packet 
+		 so reinitialize the packet data counter to zero 
+		*/
+		rbu_data.packet_write_count = 0;
+	} else {
+		rbu_data.packet_write_count += length;
+	}
+	pr_debug("fill_last_packet: exit \n");
+	return 0;
+}
+
+/*
+ get_free_pages_limited:
+ This is a helper function which allocates free pages based on an upper limit.
+ On x86_64 or 64 bit arch the memory allocation goes above 4GB space which is 
+ not addressable by the BIOS. This function tries to get allocation below the 
+ limit (4GB) address. It first tries to allocate memory normally using the 
+ GFP_KERNEL argument if the incoming limit is non-zero and if the returned 
+ physical memory address exceeds the upper limit, the allocated pages are freed 
+ and the memory is reallocated using the GFP_DMA argument.
+*/
+static void *get_free_pages_limited(unsigned long size,
+                                    int *ordernum,
+				    unsigned long limit)
+{
+	unsigned long img_buf_phys_addr;
+	void *pbuf = NULL;
+
+	*ordernum = get_order(size);
+	/* 
+         Check if we are not getting a very large file. 
+	 This can happen as a user error in entering the file size 
+        */
+	if (*ordernum == BITS_PER_LONG) {
+		pr_debug("get_free_pages_limited: Incoming size is"
+			" very large\n");
+		return NULL;
+	}
+	
+	/* try allocating a new buffer to fit the request */
+	pbuf =(unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);
+
+	if (pbuf != NULL) {
+        	/* check if the image is with in limits */
+		img_buf_phys_addr = (unsigned long)virt_to_phys(pbuf);
+		
+		if ((limit != 0) && ((img_buf_phys_addr + size) > limit)) {
+			pr_debug("Got memory above 4GB range, free this "
+				"and try with DMA memory\n");
+			/* free this memory as we need it with in 4GB range */
+			free_pages ((unsigned long)pbuf, *ordernum);
+			/* 
+                         Try allocating a new buffer from the GFP_DMA range 
+			 as it is with in 16MB range.
+			*/
+			pbuf =(unsigned char *)__get_free_pages(GFP_DMA, 
+						*ordernum);
+			if (pbuf == NULL)
+				pr_debug("Failed to get memory of size %ld "
+					"using GFP_DMA\n", size);
+		}
+	}
+	return pbuf;
+}
+
+static int create_packet(size_t length)
+{
+	struct packet_data *newpacket;
+	int ordernum = 0;
+
+	pr_debug("create_packet: entry \n");
+
+	if (rbu_data.packetsize == 0 ) {
+		pr_debug("create_packet: packetsize not specified\n");
+		return -EINVAL;
+	}
+
+	newpacket = kmalloc(sizeof(struct packet_data) ,GFP_KERNEL);
+	if(newpacket == NULL) {
+		printk(KERN_WARNING "create_packet: failed to allocate new "
+			"packet\n");
+		return -ENOMEM;
+	}
+
+	/* there is no upper limit on memory address for packetized mechanism*/
+	newpacket->data = get_free_pages_limited(rbu_data.packetsize,
+				&ordernum, 0);
+	pr_debug("create_packet: newpacket %p\n", newpacket->data);
+		
+	if(newpacket->data == NULL) {
+		printk(KERN_WARNING "create_packet: failed to allocate new "
+			"packet\n");
+		kfree(newpacket);
+		return -ENOMEM;
+	}
+
+	newpacket->ordernum = ordernum;
+	++rbu_data.num_packets;
+	/* initialize the newly created packet headers */
+	INIT_LIST_HEAD(&newpacket->list);
+	list_add_tail(&newpacket->list, &packet_data_head.list);
+	/* packets have fixed size*/
+	newpacket->length = rbu_data.packetsize;
+	
+	pr_debug("create_packet: exit \n");
+
+	return 0;
+}
+
+
+static int packetize_data(void *data, size_t length) 
+{
+	int rc = 0;
+
+	pr_debug("packetize_data : entry\n");
+	if (rbu_data.packet_write_count == 0) {
+		if ((rc = create_packet(length)) != 0 )
+			return rc;
+	}
+	/* fill data in to the packet */	
+	if ((rc = fill_last_packet(data, length)) != 0)
+		return rc;
+	
+	pr_debug("packetize_data : exit\n");
+	return rc;
+}
+
+
+/*
+ do_packet_read :
+ This is a helper function which reads the packet data of the 
+ current list.
+ data: is the incoming buffer
+ ptemp_list: points to the incoming list item
+ length: is the length of the free space in the buffer.
+ bytes_read: is the total number of bytes read already from 
+ the packet list
+ list_read_count: is the counter to keep track of the number 
+ of bytes read out of each packet.
+*/
+int do_packet_read(char *data, 
+		   struct list_head *ptemp_list, 
+		   int length,
+		   int bytes_read, 
+		   int *list_read_count)
+{
+	void *ptemp_buf;
+	struct packet_data *newpacket = NULL;
+	int bytes_copied = 0;
+	int j = 0;
+
+	newpacket = list_entry(ptemp_list,struct packet_data, list);
+	*list_read_count += newpacket->length;
+
+	if (*list_read_count > bytes_read) {
+		/* point to the start of unread data */
+		j = newpacket->length - (*list_read_count - bytes_read);
+		/* point to the offset in the packet buffer*/
+		ptemp_buf = (u8 *)newpacket->data + j;
+		/* check if there is enough room in the	incoming buffer*/
+		if (length > (*list_read_count - bytes_read)) 
+			/* copy what ever is there in this packet and move on*/
+			bytes_copied = (*list_read_count - bytes_read);
+		else 
+			/* copy the remaining */
+			bytes_copied = length;
+		memcpy(data, ptemp_buf, bytes_copied);
+	} 
+	return bytes_copied;
+}
+
+/*
+ packet_read_list:
+ This function reads the data out of the packet link list.
+ It will read data from multiple packets depending upon the 
+ size of the incoming buffer.
+ data: is the incoming buffer pointer
+ *pread_length: is the length of the incoming buffer. At return 
+ this value is adjusted to the actual size of the data read.
+*/
+static int packet_read_list(char *data, size_t *pread_length)
+{
+	struct list_head *ptemp_list;
+	int temp_count = 0;
+	int bytes_copied = 0;
+	int bytes_read = 0;
+	int remaining_bytes =0;
+	char *pdest = data;
+
+	/* check if we have any packets */
+	if (0 == rbu_data.num_packets)
+		return -ENOMEM;
+
+	remaining_bytes = *pread_length;
+	bytes_read = rbu_data.packet_read_count;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while(!list_empty(ptemp_list)) {
+		bytes_copied = do_packet_read(pdest, ptemp_list, 
+			remaining_bytes, bytes_read, &temp_count);
+		remaining_bytes -= bytes_copied;
+		bytes_read += bytes_copied;
+		pdest += bytes_copied;
+		/* 
+		 check if we reached end of buffer before reaching the 
+		 last packet
+		*/
+		if (remaining_bytes == 0)
+			break;
+
+		ptemp_list = ptemp_list->next;
+	}
+	/*finally set the bytes read */
+	*pread_length = bytes_read - rbu_data.packet_read_count;
+	rbu_data.packet_read_count = bytes_read;
+	return 0;
+}
+
+static void packet_empty_list(void)
+{
+	struct list_head *ptemp_list;
+	struct list_head *pnext_list;
+	struct packet_data *newpacket;
+
+	ptemp_list = (&packet_data_head.list)->next;
+	while(!list_empty(ptemp_list)) {
+		newpacket = list_entry(ptemp_list, struct packet_data, list);
+		pnext_list = ptemp_list->next;
+		list_del(ptemp_list);
+		ptemp_list = pnext_list;
+		/* 
+		 zero out the RBU packet memory before freeing to make sure
+		 there are no stale RBU packets left in memory 
+		*/
+		memset(newpacket->data, 0, rbu_data.packetsize);
+		free_pages((unsigned long)newpacket->data, newpacket->ordernum);
+		kfree(newpacket);
+	}
+	rbu_data.packet_write_count = 0;
+	rbu_data.packet_read_count = 0;
+	rbu_data.num_packets = 0;
+}
+
+
+/*
+ img_update_free:
+ Frees the buffer allocated for storing BIOS image
+ Always called with lock held and returned with lock held
+*/
+static void img_update_free( void)
+{
+	if (rbu_data.image_update_buffer == NULL)
+		return;
+	
+	/* 
+	 zero out this buffer before freeing it to get rid of any stale 
+	 BIOS image copied in memory.
+	*/
+	memset(rbu_data.image_update_buffer, 0, 
+		rbu_data.image_update_buffer_size);
+ 	free_pages((unsigned long)rbu_data.image_update_buffer, 
+		rbu_data.image_update_order_number);
+	/* Re-initialize the rbu_data variables after a free */
+	rbu_data.image_update_buffer = NULL;
+	rbu_data.image_update_buffer_size = 0;
+	rbu_data.bios_image_size = 0;
+}
+
+/*
+ img_update_realloc:
+ This function allocates the contiguous pages to accomodate the requested
+ size of data. The memory address and size values are stored globally and 
+ on every call to this function the new size is checked to see if more 
+ data is required than the existing size. If true the previous memory is freed
+ and new allocation is done to accomodate the new size. If the incoming size is 
+ less then than the already allocated size, then that  memory is reused.
+ This function is called with lock held and returna with lock held.
+*/
+static int img_update_realloc(unsigned long size)
+{
+	unsigned char *image_update_buffer = NULL;
+	unsigned long rc;
+	int ordernum =0;
+
+
+	/* check if the buffer of sufficient size has been already allocated */
+    if (rbu_data.image_update_buffer_size >= size) {
+		/* check for corruption */
+		if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {
+			pr_debug("img_update_realloc: corruption check "
+				"failed\n");
+			return -EINVAL;
+		}
+		/* we have a valid pre-allocated buffer with sufficient size */
+		return 0;
+    }
+
+	/* free any previously allocated buffer */
+	img_update_free();
+
+	/* 
+	 This has already been called as locked so we can now unlock 
+	 and proceed to calling get_free_pages_limited as this function
+	 can sleep
+	*/
+	spin_unlock(&rbu_data.lock);
+
+	image_update_buffer = (unsigned char *)get_free_pages_limited(size,
+		&ordernum,
+		BIOS_SCAN_LIMIT);
+	
+	/* acquire the spinlock again */
+	spin_lock(&rbu_data.lock);
+
+	if (image_update_buffer != NULL) {
+		rbu_data.image_update_buffer = image_update_buffer;
+		rbu_data.image_update_buffer_size = PAGE_SIZE << ordernum;
+		rbu_data.image_update_order_number = ordernum;
+		memset(rbu_data.image_update_buffer,0, 
+			rbu_data.image_update_buffer_size);
+		pr_debug("img_update_realloc: success\n");
+		rc = 0; 
+	} else {
+		pr_debug("Not enough memory for image update:order number = %d"
+			",size = %ld\n",ordernum, size);
+		rc = -ENOMEM;
+	}
+
+	return rc;
+} /* img_update_realloc */
+
+
+/*
+ read_packet_data_size:
+ Returns the size of an RBU packet; if no packets present returns 0
+*/
+static ssize_t read_packet_data_size(struct kobject *kobj, 
+				     char *buffer,
+   				     loff_t pos, 
+				     size_t count)
+{
+	unsigned int size = 0;
+	if (pos == 0)
+		size = sprintf(buffer, "%lu\n", rbu_data.packetsize);
+	return size;
+} 
+
+/*
+ write_packet_data_size:
+ Writes the RBU data size supplied by the user, if the 
+ data size supplied is non zero number, this function 
+ records the packet size for any packet allocations.
+ If a byte size of zero is supplied this function will free
+ the previously allocated packets.
+*/
+static ssize_t write_packet_data_size(struct kobject *kobj, 
+				      char *buffer, 
+				      loff_t pos, 
+				      size_t count)
+{
+	int retval = count;
+
+	spin_lock(&rbu_data.lock);
+	/* extract the image size */
+	sscanf(buffer, "%lu",&rbu_data.packetsize);
+	/* free the previous packet lists */
+	packet_empty_list();
+
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+/*
+ read_rbu_data_size:
+ Returns the size of an RBU image previously downloaded
+ if no image is downloaded the size returned is 0
+*/
+static ssize_t read_rbu_data_size(struct kobject *kobj, 
+				  char *buffer, 
+				  loff_t pos, 
+				  size_t count)
+{
+	unsigned int size = 0;
+	if (pos == 0)
+		size = sprintf(buffer, "%lu\n", rbu_data.bios_image_size);
+	return size;
+} 
+
+/*
+ write_rbu_data_size:
+ Writes the RBU data size supplied by the user, if the 
+ data size supplied is non zero number, this function 
+ allocates the contiguous physical memory pages for the 
+ supplied size , if it fails this function returns error.
+ If a byte size of zero is supplied this function will free
+ the previously allocated contiguous pages.
+*/
+static ssize_t write_rbu_data_size(struct kobject *kobj, 
+				   char *buffer, 
+				   loff_t pos, 
+				   size_t count)
+{
+	int retval = count;
+
+	spin_lock(&rbu_data.lock);
+	/* extract the image size */
+	sscanf(buffer, "%lu",&rbu_data.bios_image_size);
+
+	if (rbu_data.bios_image_size !=0 ) {
+		if (img_update_realloc(rbu_data.bios_image_size) < 0) {
+			pr_debug("write_rbu_data_size: failed to allocate "
+				"mem size %lu\n", 
+				(unsigned long)rbu_data.bios_image_size);
+			rbu_data.bios_image_size = 0;
+			retval = -ENOMEM;
+		} 
+	} else {
+		pr_debug(" freeing %ld size memory \n", 
+				rbu_data.bios_image_size);
+		img_update_free();
+	}
+	
+	pr_debug("write_rbu_data_size: = %lu\n", 
+		(unsigned long)rbu_data.bios_image_size);
+
+	spin_unlock(&rbu_data.lock);
+	return retval;
+} /* write_rbu_data_size*/
+
+/*
+ read_rbu_data:
+ Reads the BIOS image file from previously allocated contiguous physical 
+ pages in to the buffer supplied in this call. 
+ The reading is done in chunks of bytes supplied in the count argument.
+ The reading stops when the total number of bytes read equals the image 
+ size given previously.
+ If image size is not specified or if the image size is zero this function 
+ returns failure.
+ Parameters: 
+ kobj is the kernel object 
+ buffer is the pointer to the incoming data buffer.
+ count is the value of the incoming buffer size,
+ pos is the amount of bytes already read.
+*/
+static ssize_t read_rbu_data(struct kobject *kobj, 
+			     char *buffer,
+			     loff_t pos, 
+			     size_t count)
+{
+	unsigned char *ptemp = NULL;
+	int retval =0;
+	size_t bytes_left = 0;
+	size_t data_length = 0;
+
+	spin_lock(&rbu_data.lock);
+
+	/* check to see if we have something to return */
+	if ((rbu_data.image_update_buffer == NULL) || 
+		(rbu_data.bios_image_size == 0)) {
+		pr_debug("read_rbu_data: image_update_buffer %p ,"
+			"bios_image_size %lu\n",
+			rbu_data.image_update_buffer, 
+			rbu_data.bios_image_size);
+		retval = -ENOMEM;
+		goto read_rbu_data_exit;
+	}
+	
+	if ( pos > rbu_data.bios_image_size ) {
+		retval = 0;
+		goto read_rbu_data_exit;
+	}
+
+	bytes_left = rbu_data.bios_image_size - pos;
+	data_length = max(bytes_left,count);
+
+	ptemp = rbu_data.image_update_buffer;
+	memcpy(buffer, (ptemp + pos), data_length);
+
+	if ((pos + count) > rbu_data.bios_image_size)
+		/* this was the last copy */
+		retval = bytes_left;
+	else 
+		retval = count;
+	
+read_rbu_data_exit:
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+/*
+ write_rbu_data
+ Writes from the incoming BIOS image file to the pre-allocated 
+ contiguous physical memory pages. 
+ The writes occur in chunks of memory supplied by the count. The writes 
+ stops when the total memory supplied equals the image size given previously.
+ If no memory size is previously specified or if the previously specified size 
+ is zero the write returns error.
+*/
+static ssize_t write_rbu_data(struct kobject *kobj, 
+			      char *buffer,
+			      loff_t pos, 
+			      size_t count)
+{
+	unsigned char *pDest = NULL;
+	unsigned char *ptemp = NULL;
+	int retval = 0;
+
+	spin_lock(&rbu_data.lock);
+
+	/* check if the image size is given */
+	if (0 == rbu_data.bios_image_size) {
+		printk(KERN_WARNING "write_rbu_data: BIOS image size "
+				"not set\n");
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+
+	if ((pos + count) > rbu_data.bios_image_size) {
+		pr_debug("write_rbu_data: data_over_run, file pos %lu "
+			"bios_image_size %lu\n",
+			(unsigned long)pos,
+			rbu_data.bios_image_size);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+
+	pDest = (unsigned char*)rbu_data.image_update_buffer;
+	ptemp = pDest + pos;
+	memcpy(ptemp, buffer,  count);
+	retval = count;
+	pr_debug("write_rbu_data : retval = %d\n", retval);
+error_exit:
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+/*
+ read_rbu_packet_data:
+ Reads the BIOS image file from previously allocated contiguous physical 
+ pages in to the buffer supplied in this call. 
+ The reading is done in chunks of bytes supplied in the count argument.
+ The reading stops when the total number of bytes read equals the image 
+ size given previously.
+ If image size is not specified or if the image size is zero this function 
+ returns failure.
+ Parameters: 
+ kobj is the kernel object 
+ buffer is the pointer to the incoming data buffer.
+ count is the value of the incoming buffer size,
+ pos is the amount of bytes already read.
+*/
+static ssize_t read_rbu_packet_data(struct kobject *kobj, 
+				    char *buffer,
+				    loff_t pos, 
+				    size_t count)
+{
+	int retval;
+	size_t bytes_left;
+	size_t data_length;
+	char *ptempBuf = buffer;
+	unsigned long imagesize;
+
+	spin_lock(&rbu_data.lock);
+
+	/* check to see if we have something to return */
+	if (rbu_data.num_packets == 0) {
+		pr_debug("read_rbu_packet_data: no packets written\n");
+		retval = -ENOMEM;
+		goto read_rbu_data_exit;
+	}
+
+	imagesize = rbu_data.num_packets * rbu_data.packetsize;
+	
+	if ( pos > imagesize ) {
+		retval = 0;
+		printk(KERN_WARNING "read_rbu_packet_data: data underrun\n");
+		goto read_rbu_data_exit;
+	}
+
+	bytes_left = imagesize - pos;
+	data_length = max(bytes_left, count);
+
+	if ((retval = packet_read_list(ptempBuf, &data_length)) < 0)
+		goto read_rbu_data_exit;
+
+	if ((pos + count) > imagesize) {
+		rbu_data.packet_read_count = 0;
+		/* this was the last copy */
+		retval = bytes_left;
+	}
+	else 
+		retval = count;
+	
+read_rbu_data_exit:
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+/*
+ write_rbu_packet_data
+ Writes from the incoming BIOS image file to the pre-allocated 
+ contiguous physical memory pages. 
+ The writes occur in chunks of memory supplied by the count. The writes 
+ stops when the total memory supplied equals the image size given previously.
+ If no memory size is previously specified or if the previously specified size 
+ is zero the write returns error.
+*/
+static ssize_t write_rbu_packet_data(struct kobject *kobj, 
+				     char *buffer,
+				     loff_t pos, 
+				     size_t count)
+{
+	int retval = 0;
+
+	spin_lock(&rbu_data.lock);
+
+	/* check if the packet size is given */
+	if (0 == rbu_data.packetsize) {
+		printk(KERN_WARNING "write_rbu_packet_data: packetsize " 
+			"not set\n");
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+
+	if ((pos + count) > rbu_data.packetsize) {
+		pr_debug("write_rbu_packet_data: data_over_run, file pos %lu,"
+			"packetsize %lu\n",
+			(unsigned long)pos,
+			rbu_data.packetsize);
+		retval = -ENOMEM;
+
+		/* 
+		 We have a write data overrun, obviously this is 
+		 not the corret file, so free the previous data
+		*/
+		pr_debug("data overrun freeing all the previous packets\n");
+		packet_empty_list();
+		goto error_exit;
+	}
+
+	if ((retval = packetize_data(buffer, count)) < 0 ) {
+		pr_debug(KERN_WARNING "write_rbu_packet_data: packetize_data "
+			"failed with status %d\n", retval);
+		retval = -EIO;
+		goto error_exit;
+	} 
+
+	retval = count;
+
+	pr_debug("write_rbu_packet_data : retval = %d\n", retval);
+
+error_exit:
+	spin_unlock(&rbu_data.lock);
+	return retval;
+}
+
+
+static struct bin_attribute rbudata_attr = {
+	.attr = {.name = "rbudata", .owner = THIS_MODULE, .mode = 0644},
+	.read = read_rbu_data,
+	.write = write_rbu_data,
+};
+
+static struct bin_attribute rbudatasize_attr = {
+	.attr = { .name = "rbudatasize", .owner = THIS_MODULE, .mode = 0644},
+	.read = read_rbu_data_size,
+	.write= write_rbu_data_size,
+};
+
+static struct bin_attribute packetdatasize_attr = {
+	.attr = { .name = "packetdatasize", .owner = THIS_MODULE, .mode = 0644},
+	.read = read_packet_data_size,
+	.write= write_packet_data_size,
+};
+
+static struct bin_attribute packetdata_attr = {
+	.attr = { .name = "packetdata", .owner = THIS_MODULE, .mode = 0644},
+	.read = read_rbu_packet_data,
+	.write= write_rbu_packet_data,
+};
+
+static int __init dcdrbu_init(void)
+{
+	int rc;
+
+	spin_lock_init(&rbu_data.lock);
+
+	init_packet_head();
+	
+	rc = firmware_register(&rbu_subsys);
+	if (rc < 0) {
+		printk(KERN_WARNING "dcdrbu_init: firmware_register failed\n");
+		return rc;
+	}
+
+	sysfs_create_bin_file(&rbu_subsys.kset.kobj,&rbudata_attr);
+	sysfs_create_bin_file(&rbu_subsys.kset.kobj,&rbudatasize_attr);
+	sysfs_create_bin_file(&rbu_subsys.kset.kobj,&packetdatasize_attr);
+	sysfs_create_bin_file(&rbu_subsys.kset.kobj,&packetdata_attr);
+
+	return rc;
+}
+
+static __exit void dcdrbu_exit( void)
+{
+	spin_lock(&rbu_data.lock);
+	packet_empty_list();
+	img_update_free();
+	spin_unlock(&rbu_data.lock);
+	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &rbudata_attr);
+	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &rbudatasize_attr);
+	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &packetdatasize_attr);
+	sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &packetdata_attr);
+	firmware_unregister(&rbu_subsys);
+} 
+
+module_exit(dcdrbu_exit);
+module_init(dcdrbu_init);
+
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Kconfig linux-2.6.11.8/drivers/firmware/Kconfig
--- linux-2.6.11.8.ORIG/drivers/firmware/Kconfig	2005-05-13 12:07:58.000000000 -0500
+++ linux-2.6.11.8/drivers/firmware/Kconfig	2005-05-13 12:07:00.000000000 -0500
@@ -58,4 +58,16 @@ config EFI_PCDP
 
 	  See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf>
 
+config DELL_RBU
+        tristate "BIOS update support for DELL systems via sysfs"
+        default n
+        help
+          Say Y if you want to have the option of updating the BIOS for your
+	  DELL system. Note you need a supporting application to comunicate 
+	  with the BIOS regardign the new image for the image update to 
+	  take effect.
+
+	  See <file:Documentation/DELL_RBU.txt> for more details on the driver.
+
+
 endmenu
diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Makefile linux-2.6.11.8/drivers/firmware/Makefile
--- linux-2.6.11.8.ORIG/drivers/firmware/Makefile	2005-05-13 12:08:12.000000000 -0500
+++ linux-2.6.11.8/drivers/firmware/Makefile	2005-05-09 15:15:16.000000000 -0500
@@ -4,3 +4,4 @@
 obj-$(CONFIG_EDD)             	+= edd.o
 obj-$(CONFIG_EFI_VARS)		+= efivars.o
 obj-$(CONFIG_EFI_PCDP)		+= pcdp.o
+obj-$(CONFIG_DELL_RBU)		+= dell_rbu.o

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2005-07-20 18:52 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-05-13 19:00 [patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver Abhay Salunke
2005-05-13 21:11 ` Alexey Dobriyan
2005-05-18 18:13 Abhay Salunke
2005-05-19  3:32 ` Greg KH
2005-05-19 12:03 Abhay_Salunke
2005-05-19 14:42 ` Greg KH
2005-05-23 14:52 Abhay_Salunke
2005-05-23 14:58 ` Arjan van de Ven
2005-05-23 14:59 ` Marcel Holtmann
2005-05-23 15:48 ` Greg KH
2005-05-26 16:37 Abhay_Salunke
2005-05-26 16:56 ` Matt Domsch
2005-05-26 20:37 ` Greg KH
2005-05-26 21:36   ` Marcel Holtmann
2005-05-26 18:43 Abhay_Salunke
2005-06-02 18:36 Abhay Salunke
2005-06-02 21:44 ` Marcel Holtmann
2005-06-02 23:26 Abhay Salunke
2005-06-02 23:58 ` Marcel Holtmann
2005-06-03 12:32   ` Andreas Henriksson
2005-06-05 21:51   ` Jesper Juhl
2005-06-15 17:59 Abhay Salunke
2005-06-16 18:52 ` Greg KH
2005-06-20  0:36 ` Andrew Morton
2005-06-17 14:55 Abhay_Salunke
2005-06-17 15:29 ` Greg KH
2005-07-09  1:07 [patch 2.6.12-rc3]dell_rbu: " Abhay Salunke
2005-07-20 23:50 Abhay Salunke

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).