diff options
Diffstat (limited to 'drivers/acpi/tables.c')
-rw-r--r-- | drivers/acpi/tables.c | 67 |
1 files changed, 57 insertions, 10 deletions
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 8fccbe49612a..7553774a22b7 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c | |||
@@ -49,6 +49,15 @@ static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata; | |||
49 | 49 | ||
50 | static int acpi_apic_instance __initdata; | 50 | static int acpi_apic_instance __initdata; |
51 | 51 | ||
52 | enum acpi_subtable_type { | ||
53 | ACPI_SUBTABLE_COMMON, | ||
54 | }; | ||
55 | |||
56 | struct acpi_subtable_entry { | ||
57 | union acpi_subtable_headers *hdr; | ||
58 | enum acpi_subtable_type type; | ||
59 | }; | ||
60 | |||
52 | /* | 61 | /* |
53 | * Disable table checksum verification for the early stage due to the size | 62 | * Disable table checksum verification for the early stage due to the size |
54 | * limitation of the current x86 early mapping implementation. | 63 | * limitation of the current x86 early mapping implementation. |
@@ -217,6 +226,42 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) | |||
217 | } | 226 | } |
218 | } | 227 | } |
219 | 228 | ||
229 | static unsigned long __init | ||
230 | acpi_get_entry_type(struct acpi_subtable_entry *entry) | ||
231 | { | ||
232 | switch (entry->type) { | ||
233 | case ACPI_SUBTABLE_COMMON: | ||
234 | return entry->hdr->common.type; | ||
235 | } | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static unsigned long __init | ||
240 | acpi_get_entry_length(struct acpi_subtable_entry *entry) | ||
241 | { | ||
242 | switch (entry->type) { | ||
243 | case ACPI_SUBTABLE_COMMON: | ||
244 | return entry->hdr->common.length; | ||
245 | } | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static unsigned long __init | ||
250 | acpi_get_subtable_header_length(struct acpi_subtable_entry *entry) | ||
251 | { | ||
252 | switch (entry->type) { | ||
253 | case ACPI_SUBTABLE_COMMON: | ||
254 | return sizeof(entry->hdr->common); | ||
255 | } | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static enum acpi_subtable_type __init | ||
260 | acpi_get_subtable_type(char *id) | ||
261 | { | ||
262 | return ACPI_SUBTABLE_COMMON; | ||
263 | } | ||
264 | |||
220 | /** | 265 | /** |
221 | * acpi_parse_entries_array - for each proc_num find a suitable subtable | 266 | * acpi_parse_entries_array - for each proc_num find a suitable subtable |
222 | * | 267 | * |
@@ -246,8 +291,8 @@ acpi_parse_entries_array(char *id, unsigned long table_size, | |||
246 | struct acpi_subtable_proc *proc, int proc_num, | 291 | struct acpi_subtable_proc *proc, int proc_num, |
247 | unsigned int max_entries) | 292 | unsigned int max_entries) |
248 | { | 293 | { |
249 | struct acpi_subtable_header *entry; | 294 | struct acpi_subtable_entry entry; |
250 | unsigned long table_end; | 295 | unsigned long table_end, subtable_len, entry_len; |
251 | int count = 0; | 296 | int count = 0; |
252 | int errs = 0; | 297 | int errs = 0; |
253 | int i; | 298 | int i; |
@@ -270,19 +315,20 @@ acpi_parse_entries_array(char *id, unsigned long table_size, | |||
270 | 315 | ||
271 | /* Parse all entries looking for a match. */ | 316 | /* Parse all entries looking for a match. */ |
272 | 317 | ||
273 | entry = (struct acpi_subtable_header *) | 318 | entry.type = acpi_get_subtable_type(id); |
319 | entry.hdr = (union acpi_subtable_headers *) | ||
274 | ((unsigned long)table_header + table_size); | 320 | ((unsigned long)table_header + table_size); |
321 | subtable_len = acpi_get_subtable_header_length(&entry); | ||
275 | 322 | ||
276 | while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) < | 323 | while (((unsigned long)entry.hdr) + subtable_len < table_end) { |
277 | table_end) { | ||
278 | if (max_entries && count >= max_entries) | 324 | if (max_entries && count >= max_entries) |
279 | break; | 325 | break; |
280 | 326 | ||
281 | for (i = 0; i < proc_num; i++) { | 327 | for (i = 0; i < proc_num; i++) { |
282 | if (entry->type != proc[i].id) | 328 | if (acpi_get_entry_type(&entry) != proc[i].id) |
283 | continue; | 329 | continue; |
284 | if (!proc[i].handler || | 330 | if (!proc[i].handler || |
285 | (!errs && proc[i].handler(entry, table_end))) { | 331 | (!errs && proc[i].handler(entry.hdr, table_end))) { |
286 | errs++; | 332 | errs++; |
287 | continue; | 333 | continue; |
288 | } | 334 | } |
@@ -297,13 +343,14 @@ acpi_parse_entries_array(char *id, unsigned long table_size, | |||
297 | * If entry->length is 0, break from this loop to avoid | 343 | * If entry->length is 0, break from this loop to avoid |
298 | * infinite loop. | 344 | * infinite loop. |
299 | */ | 345 | */ |
300 | if (entry->length == 0) { | 346 | entry_len = acpi_get_entry_length(&entry); |
347 | if (entry_len == 0) { | ||
301 | pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id); | 348 | pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id); |
302 | return -EINVAL; | 349 | return -EINVAL; |
303 | } | 350 | } |
304 | 351 | ||
305 | entry = (struct acpi_subtable_header *) | 352 | entry.hdr = (union acpi_subtable_headers *) |
306 | ((unsigned long)entry + entry->length); | 353 | ((unsigned long)entry.hdr + entry_len); |
307 | } | 354 | } |
308 | 355 | ||
309 | if (max_entries && count > max_entries) { | 356 | if (max_entries && count > max_entries) { |