diff options
author | Dean Luick <dean.luick@intel.com> | 2016-12-07 22:32:15 -0500 |
---|---|---|
committer | Doug Ledford <dledford@redhat.com> | 2016-12-11 15:25:13 -0500 |
commit | 62aeddbf28fa63872e8f13f47177338b0f1fd8b5 (patch) | |
tree | 6b3b79299cb880639b8add0991e05d05908d80a3 | |
parent | 8af8d2970ed98493a2db88dfcad88b0065e55e79 (diff) |
IB/hfi1: Read new EPROM format
Add the ability to read the new EPROM format.
Reviewed-by: Easwar Hariharan <easwar.hariharan@intel.com>
Signed-off-by: Dean Luick <dean.luick@intel.com>
Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
-rw-r--r-- | drivers/infiniband/hw/hfi1/eprom.c | 211 |
1 files changed, 203 insertions, 8 deletions
diff --git a/drivers/infiniband/hw/hfi1/eprom.c b/drivers/infiniband/hw/hfi1/eprom.c index e70c223801b4..26da124c88e2 100644 --- a/drivers/infiniband/hw/hfi1/eprom.c +++ b/drivers/infiniband/hw/hfi1/eprom.c | |||
@@ -207,6 +207,40 @@ done_asic: | |||
207 | /* magic character sequence that trails an image */ | 207 | /* magic character sequence that trails an image */ |
208 | #define IMAGE_TRAIL_MAGIC "egamiAPO" | 208 | #define IMAGE_TRAIL_MAGIC "egamiAPO" |
209 | 209 | ||
210 | /* EPROM file types */ | ||
211 | #define HFI1_EFT_PLATFORM_CONFIG 2 | ||
212 | |||
213 | /* segment size - 128 KiB */ | ||
214 | #define SEG_SIZE (128 * 1024) | ||
215 | |||
216 | struct hfi1_eprom_footer { | ||
217 | u32 oprom_size; /* size of the oprom, in bytes */ | ||
218 | u16 num_table_entries; | ||
219 | u16 version; /* version of this footer */ | ||
220 | u32 magic; /* must be last */ | ||
221 | }; | ||
222 | |||
223 | struct hfi1_eprom_table_entry { | ||
224 | u32 type; /* file type */ | ||
225 | u32 offset; /* file offset from start of EPROM */ | ||
226 | u32 size; /* file size, in bytes */ | ||
227 | }; | ||
228 | |||
229 | /* | ||
230 | * Calculate the max number of table entries that will fit within a directory | ||
231 | * buffer of size 'dir_size'. | ||
232 | */ | ||
233 | #define MAX_TABLE_ENTRIES(dir_size) \ | ||
234 | (((dir_size) - sizeof(struct hfi1_eprom_footer)) / \ | ||
235 | sizeof(struct hfi1_eprom_table_entry)) | ||
236 | |||
237 | #define DIRECTORY_SIZE(n) (sizeof(struct hfi1_eprom_footer) + \ | ||
238 | (sizeof(struct hfi1_eprom_table_entry) * (n))) | ||
239 | |||
240 | #define MAGIC4(a, b, c, d) ((d) << 24 | (c) << 16 | (b) << 8 | (a)) | ||
241 | #define FOOTER_MAGIC MAGIC4('e', 'p', 'r', 'm') | ||
242 | #define FOOTER_VERSION 1 | ||
243 | |||
210 | /* | 244 | /* |
211 | * Read all of partition 1. The actual file is at the front. Adjust | 245 | * Read all of partition 1. The actual file is at the front. Adjust |
212 | * the returned size if a trailing image magic is found. | 246 | * the returned size if a trailing image magic is found. |
@@ -242,6 +276,167 @@ static int read_partition_platform_config(struct hfi1_devdata *dd, void **data, | |||
242 | } | 276 | } |
243 | 277 | ||
244 | /* | 278 | /* |
279 | * The segment magic has been checked. There is a footer and table of | ||
280 | * contents present. | ||
281 | * | ||
282 | * directory is a u32 aligned buffer of size EP_PAGE_SIZE. | ||
283 | */ | ||
284 | static int read_segment_platform_config(struct hfi1_devdata *dd, | ||
285 | void *directory, void **data, u32 *size) | ||
286 | { | ||
287 | struct hfi1_eprom_footer *footer; | ||
288 | struct hfi1_eprom_table_entry *table; | ||
289 | struct hfi1_eprom_table_entry *entry; | ||
290 | void *buffer = NULL; | ||
291 | void *table_buffer = NULL; | ||
292 | int ret, i; | ||
293 | u32 directory_size; | ||
294 | u32 seg_base, seg_offset; | ||
295 | u32 bytes_available, ncopied, to_copy; | ||
296 | |||
297 | /* the footer is at the end of the directory */ | ||
298 | footer = (struct hfi1_eprom_footer *) | ||
299 | (directory + EP_PAGE_SIZE - sizeof(*footer)); | ||
300 | |||
301 | /* make sure the structure version is supported */ | ||
302 | if (footer->version != FOOTER_VERSION) | ||
303 | return -EINVAL; | ||
304 | |||
305 | /* oprom size cannot be larger than a segment */ | ||
306 | if (footer->oprom_size >= SEG_SIZE) | ||
307 | return -EINVAL; | ||
308 | |||
309 | /* the file table must fit in a segment with the oprom */ | ||
310 | if (footer->num_table_entries > | ||
311 | MAX_TABLE_ENTRIES(SEG_SIZE - footer->oprom_size)) | ||
312 | return -EINVAL; | ||
313 | |||
314 | /* find the file table start, which precedes the footer */ | ||
315 | directory_size = DIRECTORY_SIZE(footer->num_table_entries); | ||
316 | if (directory_size <= EP_PAGE_SIZE) { | ||
317 | /* the file table fits into the directory buffer handed in */ | ||
318 | table = (struct hfi1_eprom_table_entry *) | ||
319 | (directory + EP_PAGE_SIZE - directory_size); | ||
320 | } else { | ||
321 | /* need to allocate and read more */ | ||
322 | table_buffer = kmalloc(directory_size, GFP_KERNEL); | ||
323 | if (!table_buffer) | ||
324 | return -ENOMEM; | ||
325 | ret = read_length(dd, SEG_SIZE - directory_size, | ||
326 | directory_size, table_buffer); | ||
327 | if (ret) | ||
328 | goto done; | ||
329 | table = table_buffer; | ||
330 | } | ||
331 | |||
332 | /* look for the platform configuration file in the table */ | ||
333 | for (entry = NULL, i = 0; i < footer->num_table_entries; i++) { | ||
334 | if (table[i].type == HFI1_EFT_PLATFORM_CONFIG) { | ||
335 | entry = &table[i]; | ||
336 | break; | ||
337 | } | ||
338 | } | ||
339 | if (!entry) { | ||
340 | ret = -ENOENT; | ||
341 | goto done; | ||
342 | } | ||
343 | |||
344 | /* | ||
345 | * Sanity check on the configuration file size - it should never | ||
346 | * be larger than 4 KiB. | ||
347 | */ | ||
348 | if (entry->size > (4 * 1024)) { | ||
349 | dd_dev_err(dd, "Bad configuration file size 0x%x\n", | ||
350 | entry->size); | ||
351 | ret = -EINVAL; | ||
352 | goto done; | ||
353 | } | ||
354 | |||
355 | /* check for bogus offset and size that wrap when added together */ | ||
356 | if (entry->offset + entry->size < entry->offset) { | ||
357 | dd_dev_err(dd, | ||
358 | "Bad configuration file start + size 0x%x+0x%x\n", | ||
359 | entry->offset, entry->size); | ||
360 | ret = -EINVAL; | ||
361 | goto done; | ||
362 | } | ||
363 | |||
364 | /* allocate the buffer to return */ | ||
365 | buffer = kmalloc(entry->size, GFP_KERNEL); | ||
366 | if (!buffer) { | ||
367 | ret = -ENOMEM; | ||
368 | goto done; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * Extract the file by looping over segments until it is fully read. | ||
373 | */ | ||
374 | seg_offset = entry->offset % SEG_SIZE; | ||
375 | seg_base = entry->offset - seg_offset; | ||
376 | ncopied = 0; | ||
377 | while (ncopied < entry->size) { | ||
378 | /* calculate data bytes available in this segment */ | ||
379 | |||
380 | /* start with the bytes from the current offset to the end */ | ||
381 | bytes_available = SEG_SIZE - seg_offset; | ||
382 | /* subtract off footer and table from segment 0 */ | ||
383 | if (seg_base == 0) { | ||
384 | /* | ||
385 | * Sanity check: should not have a starting point | ||
386 | * at or within the directory. | ||
387 | */ | ||
388 | if (bytes_available <= directory_size) { | ||
389 | dd_dev_err(dd, | ||
390 | "Bad configuration file - offset 0x%x within footer+table\n", | ||
391 | entry->offset); | ||
392 | ret = -EINVAL; | ||
393 | goto done; | ||
394 | } | ||
395 | bytes_available -= directory_size; | ||
396 | } | ||
397 | |||
398 | /* calculate bytes wanted */ | ||
399 | to_copy = entry->size - ncopied; | ||
400 | |||
401 | /* max out at the available bytes in this segment */ | ||
402 | if (to_copy > bytes_available) | ||
403 | to_copy = bytes_available; | ||
404 | |||
405 | /* | ||
406 | * Read from the EPROM. | ||
407 | * | ||
408 | * The sanity check for entry->offset is done in read_length(). | ||
409 | * The EPROM offset is validated against what the hardware | ||
410 | * addressing supports. In addition, if the offset is larger | ||
411 | * than the actual EPROM, it silently wraps. It will work | ||
412 | * fine, though the reader may not get what they expected | ||
413 | * from the EPROM. | ||
414 | */ | ||
415 | ret = read_length(dd, seg_base + seg_offset, to_copy, | ||
416 | buffer + ncopied); | ||
417 | if (ret) | ||
418 | goto done; | ||
419 | |||
420 | ncopied += to_copy; | ||
421 | |||
422 | /* set up for next segment */ | ||
423 | seg_offset = footer->oprom_size; | ||
424 | seg_base += SEG_SIZE; | ||
425 | } | ||
426 | |||
427 | /* success */ | ||
428 | ret = 0; | ||
429 | *data = buffer; | ||
430 | *size = entry->size; | ||
431 | |||
432 | done: | ||
433 | kfree(table_buffer); | ||
434 | if (ret) | ||
435 | kfree(buffer); | ||
436 | return ret; | ||
437 | } | ||
438 | |||
439 | /* | ||
245 | * Read the platform configuration file from the EPROM. | 440 | * Read the platform configuration file from the EPROM. |
246 | * | 441 | * |
247 | * On success, an allocated buffer containing the data and its size are | 442 | * On success, an allocated buffer containing the data and its size are |
@@ -253,6 +448,7 @@ static int read_partition_platform_config(struct hfi1_devdata *dd, void **data, | |||
253 | * -EBUSY - not able to acquire access to the EPROM | 448 | * -EBUSY - not able to acquire access to the EPROM |
254 | * -ENOENT - no recognizable file written | 449 | * -ENOENT - no recognizable file written |
255 | * -ENOMEM - buffer could not be allocated | 450 | * -ENOMEM - buffer could not be allocated |
451 | * -EINVAL - invalid EPROM contentents found | ||
256 | */ | 452 | */ |
257 | int eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size) | 453 | int eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size) |
258 | { | 454 | { |
@@ -266,21 +462,20 @@ int eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size) | |||
266 | if (ret) | 462 | if (ret) |
267 | return -EBUSY; | 463 | return -EBUSY; |
268 | 464 | ||
269 | /* read the last page of P0 for the EPROM format magic */ | 465 | /* read the last page of the segment for the EPROM format magic */ |
270 | ret = read_length(dd, P1_START - EP_PAGE_SIZE, EP_PAGE_SIZE, directory); | 466 | ret = read_length(dd, SEG_SIZE - EP_PAGE_SIZE, EP_PAGE_SIZE, directory); |
271 | if (ret) | 467 | if (ret) |
272 | goto done; | 468 | goto done; |
273 | 469 | ||
274 | /* last dword of P0 contains a magic indicator */ | 470 | /* last dword of the segment contains a magic value */ |
275 | if (directory[EP_PAGE_DWORDS - 1] == 0) { | 471 | if (directory[EP_PAGE_DWORDS - 1] == FOOTER_MAGIC) { |
472 | /* segment format */ | ||
473 | ret = read_segment_platform_config(dd, directory, data, size); | ||
474 | } else { | ||
276 | /* partition format */ | 475 | /* partition format */ |
277 | ret = read_partition_platform_config(dd, data, size); | 476 | ret = read_partition_platform_config(dd, data, size); |
278 | goto done; | ||
279 | } | 477 | } |
280 | 478 | ||
281 | /* nothing recognized */ | ||
282 | ret = -ENOENT; | ||
283 | |||
284 | done: | 479 | done: |
285 | release_chip_resource(dd, CR_EPROM); | 480 | release_chip_resource(dd, CR_EPROM); |
286 | return ret; | 481 | return ret; |