aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/dell_rbu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware/dell_rbu.c')
-rw-r--r--drivers/firmware/dell_rbu.c121
1 files changed, 95 insertions, 26 deletions
diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c
index 125929c9048f..ba17292eb290 100644
--- a/drivers/firmware/dell_rbu.c
+++ b/drivers/firmware/dell_rbu.c
@@ -50,7 +50,7 @@
50MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>"); 50MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
51MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); 51MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
52MODULE_LICENSE("GPL"); 52MODULE_LICENSE("GPL");
53MODULE_VERSION("3.0"); 53MODULE_VERSION("3.1");
54 54
55#define BIOS_SCAN_LIMIT 0xffffffff 55#define BIOS_SCAN_LIMIT 0xffffffff
56#define MAX_IMAGE_LENGTH 16 56#define MAX_IMAGE_LENGTH 16
@@ -73,6 +73,11 @@ module_param_string(image_type, image_type, sizeof (image_type), 0);
73MODULE_PARM_DESC(image_type, 73MODULE_PARM_DESC(image_type,
74 "BIOS image type. choose- mono or packet or init"); 74 "BIOS image type. choose- mono or packet or init");
75 75
76static unsigned long allocation_floor = 0x100000;
77module_param(allocation_floor, ulong, 0644);
78MODULE_PARM_DESC(allocation_floor,
79 "Minimum address for allocations when using Packet mode");
80
76struct packet_data { 81struct packet_data {
77 struct list_head list; 82 struct list_head list;
78 size_t length; 83 size_t length;
@@ -99,61 +104,122 @@ static int create_packet(void *data, size_t length)
99{ 104{
100 struct packet_data *newpacket; 105 struct packet_data *newpacket;
101 int ordernum = 0; 106 int ordernum = 0;
107 int retval = 0;
108 unsigned int packet_array_size = 0;
109 void **invalid_addr_packet_array = 0;
110 void *packet_data_temp_buf = 0;
111 unsigned int idx = 0;
102 112
103 pr_debug("create_packet: entry \n"); 113 pr_debug("create_packet: entry \n");
104 114
105 if (!rbu_data.packetsize) { 115 if (!rbu_data.packetsize) {
106 pr_debug("create_packet: packetsize not specified\n"); 116 pr_debug("create_packet: packetsize not specified\n");
107 return -EINVAL; 117 retval = -EINVAL;
118 goto out_noalloc;
108 } 119 }
120
109 spin_unlock(&rbu_data.lock); 121 spin_unlock(&rbu_data.lock);
110 newpacket = kmalloc(sizeof (struct packet_data), GFP_KERNEL); 122
111 spin_lock(&rbu_data.lock); 123 newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL);
112 124
113 if (!newpacket) { 125 if (!newpacket) {
114 printk(KERN_WARNING 126 printk(KERN_WARNING
115 "dell_rbu:%s: failed to allocate new " 127 "dell_rbu:%s: failed to allocate new "
116 "packet\n", __FUNCTION__); 128 "packet\n", __FUNCTION__);
117 return -ENOMEM; 129 retval = -ENOMEM;
130 spin_lock(&rbu_data.lock);
131 goto out_noalloc;
118 } 132 }
119 133
120 ordernum = get_order(length); 134 ordernum = get_order(length);
135
121 /* 136 /*
122 * there is no upper limit on memory 137 * BIOS errata mean we cannot allocate packets below 1MB or they will
123 * address for packetized mechanism 138 * be overwritten by BIOS.
139 *
140 * array to temporarily hold packets
141 * that are below the allocation floor
142 *
143 * NOTE: very simplistic because we only need the floor to be at 1MB
144 * due to BIOS errata. This shouldn't be used for higher floors
145 * or you will run out of mem trying to allocate the array.
124 */ 146 */
125 spin_unlock(&rbu_data.lock); 147 packet_array_size = max(
126 newpacket->data = (unsigned char *) __get_free_pages(GFP_KERNEL, 148 (unsigned int)(allocation_floor / rbu_data.packetsize),
127 ordernum); 149 (unsigned int)1);
128 spin_lock(&rbu_data.lock); 150 invalid_addr_packet_array = kzalloc(packet_array_size * sizeof(void*),
151 GFP_KERNEL);
129 152
130 pr_debug("create_packet: newpacket %p\n", newpacket->data); 153 if (!invalid_addr_packet_array) {
131
132 if (!newpacket->data) {
133 printk(KERN_WARNING 154 printk(KERN_WARNING
134 "dell_rbu:%s: failed to allocate new " 155 "dell_rbu:%s: failed to allocate "
135 "packet\n", __FUNCTION__); 156 "invalid_addr_packet_array \n",
136 kfree(newpacket); 157 __FUNCTION__);
137 return -ENOMEM; 158 retval = -ENOMEM;
159 spin_lock(&rbu_data.lock);
160 goto out_alloc_packet;
138 } 161 }
139 162
163 while (!packet_data_temp_buf) {
164 packet_data_temp_buf = (unsigned char *)
165 __get_free_pages(GFP_KERNEL, ordernum);
166 if (!packet_data_temp_buf) {
167 printk(KERN_WARNING
168 "dell_rbu:%s: failed to allocate new "
169 "packet\n", __FUNCTION__);
170 retval = -ENOMEM;
171 spin_lock(&rbu_data.lock);
172 goto out_alloc_packet_array;
173 }
174
175 if ((unsigned long)virt_to_phys(packet_data_temp_buf)
176 < allocation_floor) {
177 pr_debug("packet 0x%lx below floor at 0x%lx.\n",
178 (unsigned long)virt_to_phys(
179 packet_data_temp_buf),
180 allocation_floor);
181 invalid_addr_packet_array[idx++] = packet_data_temp_buf;
182 packet_data_temp_buf = 0;
183 }
184 }
185 spin_lock(&rbu_data.lock);
186
187 newpacket->data = packet_data_temp_buf;
188
189 pr_debug("create_packet: newpacket at physical addr %lx\n",
190 (unsigned long)virt_to_phys(newpacket->data));
191
192 /* packets may not have fixed size */
193 newpacket->length = length;
140 newpacket->ordernum = ordernum; 194 newpacket->ordernum = ordernum;
141 ++rbu_data.num_packets; 195 ++rbu_data.num_packets;
142 /* 196
143 * initialize the newly created packet headers 197 /* initialize the newly created packet headers */
144 */
145 INIT_LIST_HEAD(&newpacket->list); 198 INIT_LIST_HEAD(&newpacket->list);
146 list_add_tail(&newpacket->list, &packet_data_head.list); 199 list_add_tail(&newpacket->list, &packet_data_head.list);
147 /*
148 * packets may not have fixed size
149 */
150 newpacket->length = length;
151 200
152 memcpy(newpacket->data, data, length); 201 memcpy(newpacket->data, data, length);
153 202
154 pr_debug("create_packet: exit \n"); 203 pr_debug("create_packet: exit \n");
155 204
156 return 0; 205out_alloc_packet_array:
206 /* always free packet array */
207 for (;idx>0;idx--) {
208 pr_debug("freeing unused packet below floor 0x%lx.\n",
209 (unsigned long)virt_to_phys(
210 invalid_addr_packet_array[idx-1]));
211 free_pages((unsigned long)invalid_addr_packet_array[idx-1],
212 ordernum);
213 }
214 kfree(invalid_addr_packet_array);
215
216out_alloc_packet:
217 /* if error, free data */
218 if (retval)
219 kfree(newpacket);
220
221out_noalloc:
222 return retval;
157} 223}
158 224
159static int packetize_data(void *data, size_t length) 225static int packetize_data(void *data, size_t length)
@@ -693,3 +759,6 @@ static __exit void dcdrbu_exit(void)
693 759
694module_exit(dcdrbu_exit); 760module_exit(dcdrbu_exit);
695module_init(dcdrbu_init); 761module_init(dcdrbu_init);
762
763/* vim:noet:ts=8:sw=8
764*/