diff options
Diffstat (limited to 'drivers/firmware/dell_rbu.c')
-rw-r--r-- | drivers/firmware/dell_rbu.c | 121 |
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 @@ | |||
50 | MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>"); | 50 | MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>"); |
51 | MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); | 51 | MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); |
52 | MODULE_LICENSE("GPL"); | 52 | MODULE_LICENSE("GPL"); |
53 | MODULE_VERSION("3.0"); | 53 | MODULE_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); | |||
73 | MODULE_PARM_DESC(image_type, | 73 | MODULE_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 | ||
76 | static unsigned long allocation_floor = 0x100000; | ||
77 | module_param(allocation_floor, ulong, 0644); | ||
78 | MODULE_PARM_DESC(allocation_floor, | ||
79 | "Minimum address for allocations when using Packet mode"); | ||
80 | |||
76 | struct packet_data { | 81 | struct 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; | 205 | out_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 | |||
216 | out_alloc_packet: | ||
217 | /* if error, free data */ | ||
218 | if (retval) | ||
219 | kfree(newpacket); | ||
220 | |||
221 | out_noalloc: | ||
222 | return retval; | ||
157 | } | 223 | } |
158 | 224 | ||
159 | static int packetize_data(void *data, size_t length) | 225 | static int packetize_data(void *data, size_t length) |
@@ -693,3 +759,6 @@ static __exit void dcdrbu_exit(void) | |||
693 | 759 | ||
694 | module_exit(dcdrbu_exit); | 760 | module_exit(dcdrbu_exit); |
695 | module_init(dcdrbu_init); | 761 | module_init(dcdrbu_init); |
762 | |||
763 | /* vim:noet:ts=8:sw=8 | ||
764 | */ | ||