|
@@ -50,7 +50,7 @@
|
|
|
MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
|
|
|
MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
-MODULE_VERSION("3.0");
|
|
|
+MODULE_VERSION("3.1");
|
|
|
|
|
|
#define BIOS_SCAN_LIMIT 0xffffffff
|
|
|
#define MAX_IMAGE_LENGTH 16
|
|
@@ -73,6 +73,11 @@ module_param_string(image_type, image_type, sizeof (image_type), 0);
|
|
|
MODULE_PARM_DESC(image_type,
|
|
|
"BIOS image type. choose- mono or packet or init");
|
|
|
|
|
|
+static unsigned long allocation_floor = 0x100000;
|
|
|
+module_param(allocation_floor, ulong, 0644);
|
|
|
+MODULE_PARM_DESC(allocation_floor,
|
|
|
+ "Minimum address for allocations when using Packet mode");
|
|
|
+
|
|
|
struct packet_data {
|
|
|
struct list_head list;
|
|
|
size_t length;
|
|
@@ -99,61 +104,122 @@ static int create_packet(void *data, size_t length)
|
|
|
{
|
|
|
struct packet_data *newpacket;
|
|
|
int ordernum = 0;
|
|
|
+ int retval = 0;
|
|
|
+ unsigned int packet_array_size = 0;
|
|
|
+ void **invalid_addr_packet_array = 0;
|
|
|
+ void *packet_data_temp_buf = 0;
|
|
|
+ unsigned int idx = 0;
|
|
|
|
|
|
pr_debug("create_packet: entry \n");
|
|
|
|
|
|
if (!rbu_data.packetsize) {
|
|
|
pr_debug("create_packet: packetsize not specified\n");
|
|
|
- return -EINVAL;
|
|
|
+ retval = -EINVAL;
|
|
|
+ goto out_noalloc;
|
|
|
}
|
|
|
+
|
|
|
spin_unlock(&rbu_data.lock);
|
|
|
- newpacket = kmalloc(sizeof (struct packet_data), GFP_KERNEL);
|
|
|
- spin_lock(&rbu_data.lock);
|
|
|
+
|
|
|
+ newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL);
|
|
|
|
|
|
if (!newpacket) {
|
|
|
printk(KERN_WARNING
|
|
|
"dell_rbu:%s: failed to allocate new "
|
|
|
"packet\n", __FUNCTION__);
|
|
|
- return -ENOMEM;
|
|
|
+ retval = -ENOMEM;
|
|
|
+ spin_lock(&rbu_data.lock);
|
|
|
+ goto out_noalloc;
|
|
|
}
|
|
|
|
|
|
ordernum = get_order(length);
|
|
|
+
|
|
|
/*
|
|
|
- * there is no upper limit on memory
|
|
|
- * address for packetized mechanism
|
|
|
+ * BIOS errata mean we cannot allocate packets below 1MB or they will
|
|
|
+ * be overwritten by BIOS.
|
|
|
+ *
|
|
|
+ * array to temporarily hold packets
|
|
|
+ * that are below the allocation floor
|
|
|
+ *
|
|
|
+ * NOTE: very simplistic because we only need the floor to be at 1MB
|
|
|
+ * due to BIOS errata. This shouldn't be used for higher floors
|
|
|
+ * or you will run out of mem trying to allocate the array.
|
|
|
*/
|
|
|
- spin_unlock(&rbu_data.lock);
|
|
|
- newpacket->data = (unsigned char *) __get_free_pages(GFP_KERNEL,
|
|
|
- ordernum);
|
|
|
- spin_lock(&rbu_data.lock);
|
|
|
+ packet_array_size = max(
|
|
|
+ (unsigned int)(allocation_floor / rbu_data.packetsize),
|
|
|
+ (unsigned int)1);
|
|
|
+ invalid_addr_packet_array = kzalloc(packet_array_size * sizeof(void*),
|
|
|
+ GFP_KERNEL);
|
|
|
|
|
|
- pr_debug("create_packet: newpacket %p\n", newpacket->data);
|
|
|
-
|
|
|
- if (!newpacket->data) {
|
|
|
+ if (!invalid_addr_packet_array) {
|
|
|
printk(KERN_WARNING
|
|
|
- "dell_rbu:%s: failed to allocate new "
|
|
|
- "packet\n", __FUNCTION__);
|
|
|
- kfree(newpacket);
|
|
|
- return -ENOMEM;
|
|
|
+ "dell_rbu:%s: failed to allocate "
|
|
|
+ "invalid_addr_packet_array \n",
|
|
|
+ __FUNCTION__);
|
|
|
+ retval = -ENOMEM;
|
|
|
+ spin_lock(&rbu_data.lock);
|
|
|
+ goto out_alloc_packet;
|
|
|
}
|
|
|
|
|
|
+ while (!packet_data_temp_buf) {
|
|
|
+ packet_data_temp_buf = (unsigned char *)
|
|
|
+ __get_free_pages(GFP_KERNEL, ordernum);
|
|
|
+ if (!packet_data_temp_buf) {
|
|
|
+ printk(KERN_WARNING
|
|
|
+ "dell_rbu:%s: failed to allocate new "
|
|
|
+ "packet\n", __FUNCTION__);
|
|
|
+ retval = -ENOMEM;
|
|
|
+ spin_lock(&rbu_data.lock);
|
|
|
+ goto out_alloc_packet_array;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((unsigned long)virt_to_phys(packet_data_temp_buf)
|
|
|
+ < allocation_floor) {
|
|
|
+ pr_debug("packet 0x%lx below floor at 0x%lx.\n",
|
|
|
+ (unsigned long)virt_to_phys(
|
|
|
+ packet_data_temp_buf),
|
|
|
+ allocation_floor);
|
|
|
+ invalid_addr_packet_array[idx++] = packet_data_temp_buf;
|
|
|
+ packet_data_temp_buf = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_lock(&rbu_data.lock);
|
|
|
+
|
|
|
+ newpacket->data = packet_data_temp_buf;
|
|
|
+
|
|
|
+ pr_debug("create_packet: newpacket at physical addr %lx\n",
|
|
|
+ (unsigned long)virt_to_phys(newpacket->data));
|
|
|
+
|
|
|
+ /* packets may not have fixed size */
|
|
|
+ newpacket->length = length;
|
|
|
newpacket->ordernum = ordernum;
|
|
|
++rbu_data.num_packets;
|
|
|
- /*
|
|
|
- * initialize the newly created packet headers
|
|
|
- */
|
|
|
+
|
|
|
+ /* initialize the newly created packet headers */
|
|
|
INIT_LIST_HEAD(&newpacket->list);
|
|
|
list_add_tail(&newpacket->list, &packet_data_head.list);
|
|
|
- /*
|
|
|
- * packets may not have fixed size
|
|
|
- */
|
|
|
- newpacket->length = length;
|
|
|
|
|
|
memcpy(newpacket->data, data, length);
|
|
|
|
|
|
pr_debug("create_packet: exit \n");
|
|
|
|
|
|
- return 0;
|
|
|
+out_alloc_packet_array:
|
|
|
+ /* always free packet array */
|
|
|
+ for (;idx>0;idx--) {
|
|
|
+ pr_debug("freeing unused packet below floor 0x%lx.\n",
|
|
|
+ (unsigned long)virt_to_phys(
|
|
|
+ invalid_addr_packet_array[idx-1]));
|
|
|
+ free_pages((unsigned long)invalid_addr_packet_array[idx-1],
|
|
|
+ ordernum);
|
|
|
+ }
|
|
|
+ kfree(invalid_addr_packet_array);
|
|
|
+
|
|
|
+out_alloc_packet:
|
|
|
+ /* if error, free data */
|
|
|
+ if (retval)
|
|
|
+ kfree(newpacket);
|
|
|
+
|
|
|
+out_noalloc:
|
|
|
+ return retval;
|
|
|
}
|
|
|
|
|
|
static int packetize_data(void *data, size_t length)
|
|
@@ -693,3 +759,6 @@ static __exit void dcdrbu_exit(void)
|
|
|
|
|
|
module_exit(dcdrbu_exit);
|
|
|
module_init(dcdrbu_init);
|
|
|
+
|
|
|
+/* vim:noet:ts=8:sw=8
|
|
|
+*/
|