eprom.c (11519B)
1// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause 2/* 3 * Copyright(c) 2015, 2016 Intel Corporation. 4 */ 5 6#include <linux/delay.h> 7#include "hfi.h" 8#include "common.h" 9#include "eprom.h" 10 11/* 12 * The EPROM is logically divided into three partitions: 13 * partition 0: the first 128K, visible from PCI ROM BAR 14 * partition 1: 4K config file (sector size) 15 * partition 2: the rest 16 */ 17#define P0_SIZE (128 * 1024) 18#define P1_SIZE (4 * 1024) 19#define P1_START P0_SIZE 20#define P2_START (P0_SIZE + P1_SIZE) 21 22/* controller page size, in bytes */ 23#define EP_PAGE_SIZE 256 24#define EP_PAGE_MASK (EP_PAGE_SIZE - 1) 25#define EP_PAGE_DWORDS (EP_PAGE_SIZE / sizeof(u32)) 26 27/* controller commands */ 28#define CMD_SHIFT 24 29#define CMD_NOP (0) 30#define CMD_READ_DATA(addr) ((0x03 << CMD_SHIFT) | addr) 31#define CMD_RELEASE_POWERDOWN_NOID ((0xab << CMD_SHIFT)) 32 33/* controller interface speeds */ 34#define EP_SPEED_FULL 0x2 /* full speed */ 35 36/* 37 * How long to wait for the EPROM to become available, in ms. 38 * The spec 32 Mb EPROM takes around 40s to erase then write. 39 * Double it for safety. 40 */ 41#define EPROM_TIMEOUT 80000 /* ms */ 42 43/* 44 * Read a 256 byte (64 dword) EPROM page. 45 * All callers have verified the offset is at a page boundary. 46 */ 47static void read_page(struct hfi1_devdata *dd, u32 offset, u32 *result) 48{ 49 int i; 50 51 write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_DATA(offset)); 52 for (i = 0; i < EP_PAGE_DWORDS; i++) 53 result[i] = (u32)read_csr(dd, ASIC_EEP_DATA); 54 write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP); /* close open page */ 55} 56 57/* 58 * Read length bytes starting at offset from the start of the EPROM. 59 */ 60static int read_length(struct hfi1_devdata *dd, u32 start, u32 len, void *dest) 61{ 62 u32 buffer[EP_PAGE_DWORDS]; 63 u32 end; 64 u32 start_offset; 65 u32 read_start; 66 u32 bytes; 67 68 if (len == 0) 69 return 0; 70 71 end = start + len; 72 73 /* 74 * Make sure the read range is not outside of the controller read 75 * command address range. Note that '>' is correct below - the end 76 * of the range is OK if it stops at the limit, but no higher. 77 */ 78 if (end > (1 << CMD_SHIFT)) 79 return -EINVAL; 80 81 /* read the first partial page */ 82 start_offset = start & EP_PAGE_MASK; 83 if (start_offset) { 84 /* partial starting page */ 85 86 /* align and read the page that contains the start */ 87 read_start = start & ~EP_PAGE_MASK; 88 read_page(dd, read_start, buffer); 89 90 /* the rest of the page is available data */ 91 bytes = EP_PAGE_SIZE - start_offset; 92 93 if (len <= bytes) { 94 /* end is within this page */ 95 memcpy(dest, (u8 *)buffer + start_offset, len); 96 return 0; 97 } 98 99 memcpy(dest, (u8 *)buffer + start_offset, bytes); 100 101 start += bytes; 102 len -= bytes; 103 dest += bytes; 104 } 105 /* start is now page aligned */ 106 107 /* read whole pages */ 108 while (len >= EP_PAGE_SIZE) { 109 read_page(dd, start, buffer); 110 memcpy(dest, buffer, EP_PAGE_SIZE); 111 112 start += EP_PAGE_SIZE; 113 len -= EP_PAGE_SIZE; 114 dest += EP_PAGE_SIZE; 115 } 116 117 /* read the last partial page */ 118 if (len) { 119 read_page(dd, start, buffer); 120 memcpy(dest, buffer, len); 121 } 122 123 return 0; 124} 125 126/* 127 * Initialize the EPROM handler. 128 */ 129int eprom_init(struct hfi1_devdata *dd) 130{ 131 int ret = 0; 132 133 /* only the discrete chip has an EPROM */ 134 if (dd->pcidev->device != PCI_DEVICE_ID_INTEL0) 135 return 0; 136 137 /* 138 * It is OK if both HFIs reset the EPROM as long as they don't 139 * do it at the same time. 140 */ 141 ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); 142 if (ret) { 143 dd_dev_err(dd, 144 "%s: unable to acquire EPROM resource, no EPROM support\n", 145 __func__); 146 goto done_asic; 147 } 148 149 /* reset EPROM to be sure it is in a good state */ 150 151 /* set reset */ 152 write_csr(dd, ASIC_EEP_CTL_STAT, ASIC_EEP_CTL_STAT_EP_RESET_SMASK); 153 /* clear reset, set speed */ 154 write_csr(dd, ASIC_EEP_CTL_STAT, 155 EP_SPEED_FULL << ASIC_EEP_CTL_STAT_RATE_SPI_SHIFT); 156 157 /* wake the device with command "release powerdown NoID" */ 158 write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_RELEASE_POWERDOWN_NOID); 159 160 dd->eprom_available = true; 161 release_chip_resource(dd, CR_EPROM); 162done_asic: 163 return ret; 164} 165 166/* magic character sequence that begins an image */ 167#define IMAGE_START_MAGIC "APO=" 168 169/* magic character sequence that might trail an image */ 170#define IMAGE_TRAIL_MAGIC "egamiAPO" 171 172/* EPROM file types */ 173#define HFI1_EFT_PLATFORM_CONFIG 2 174 175/* segment size - 128 KiB */ 176#define SEG_SIZE (128 * 1024) 177 178struct hfi1_eprom_footer { 179 u32 oprom_size; /* size of the oprom, in bytes */ 180 u16 num_table_entries; 181 u16 version; /* version of this footer */ 182 u32 magic; /* must be last */ 183}; 184 185struct hfi1_eprom_table_entry { 186 u32 type; /* file type */ 187 u32 offset; /* file offset from start of EPROM */ 188 u32 size; /* file size, in bytes */ 189}; 190 191/* 192 * Calculate the max number of table entries that will fit within a directory 193 * buffer of size 'dir_size'. 194 */ 195#define MAX_TABLE_ENTRIES(dir_size) \ 196 (((dir_size) - sizeof(struct hfi1_eprom_footer)) / \ 197 sizeof(struct hfi1_eprom_table_entry)) 198 199#define DIRECTORY_SIZE(n) (sizeof(struct hfi1_eprom_footer) + \ 200 (sizeof(struct hfi1_eprom_table_entry) * (n))) 201 202#define MAGIC4(a, b, c, d) ((d) << 24 | (c) << 16 | (b) << 8 | (a)) 203#define FOOTER_MAGIC MAGIC4('e', 'p', 'r', 'm') 204#define FOOTER_VERSION 1 205 206/* 207 * Read all of partition 1. The actual file is at the front. Adjust 208 * the returned size if a trailing image magic is found. 209 */ 210static int read_partition_platform_config(struct hfi1_devdata *dd, void **data, 211 u32 *size) 212{ 213 void *buffer; 214 void *p; 215 u32 length; 216 int ret; 217 218 buffer = kmalloc(P1_SIZE, GFP_KERNEL); 219 if (!buffer) 220 return -ENOMEM; 221 222 ret = read_length(dd, P1_START, P1_SIZE, buffer); 223 if (ret) { 224 kfree(buffer); 225 return ret; 226 } 227 228 /* config partition is valid only if it starts with IMAGE_START_MAGIC */ 229 if (memcmp(buffer, IMAGE_START_MAGIC, strlen(IMAGE_START_MAGIC))) { 230 kfree(buffer); 231 return -ENOENT; 232 } 233 234 /* scan for image magic that may trail the actual data */ 235 p = strnstr(buffer, IMAGE_TRAIL_MAGIC, P1_SIZE); 236 if (p) 237 length = p - buffer; 238 else 239 length = P1_SIZE; 240 241 *data = buffer; 242 *size = length; 243 return 0; 244} 245 246/* 247 * The segment magic has been checked. There is a footer and table of 248 * contents present. 249 * 250 * directory is a u32 aligned buffer of size EP_PAGE_SIZE. 251 */ 252static int read_segment_platform_config(struct hfi1_devdata *dd, 253 void *directory, void **data, u32 *size) 254{ 255 struct hfi1_eprom_footer *footer; 256 struct hfi1_eprom_table_entry *table; 257 struct hfi1_eprom_table_entry *entry; 258 void *buffer = NULL; 259 void *table_buffer = NULL; 260 int ret, i; 261 u32 directory_size; 262 u32 seg_base, seg_offset; 263 u32 bytes_available, ncopied, to_copy; 264 265 /* the footer is at the end of the directory */ 266 footer = (struct hfi1_eprom_footer *) 267 (directory + EP_PAGE_SIZE - sizeof(*footer)); 268 269 /* make sure the structure version is supported */ 270 if (footer->version != FOOTER_VERSION) 271 return -EINVAL; 272 273 /* oprom size cannot be larger than a segment */ 274 if (footer->oprom_size >= SEG_SIZE) 275 return -EINVAL; 276 277 /* the file table must fit in a segment with the oprom */ 278 if (footer->num_table_entries > 279 MAX_TABLE_ENTRIES(SEG_SIZE - footer->oprom_size)) 280 return -EINVAL; 281 282 /* find the file table start, which precedes the footer */ 283 directory_size = DIRECTORY_SIZE(footer->num_table_entries); 284 if (directory_size <= EP_PAGE_SIZE) { 285 /* the file table fits into the directory buffer handed in */ 286 table = (struct hfi1_eprom_table_entry *) 287 (directory + EP_PAGE_SIZE - directory_size); 288 } else { 289 /* need to allocate and read more */ 290 table_buffer = kmalloc(directory_size, GFP_KERNEL); 291 if (!table_buffer) 292 return -ENOMEM; 293 ret = read_length(dd, SEG_SIZE - directory_size, 294 directory_size, table_buffer); 295 if (ret) 296 goto done; 297 table = table_buffer; 298 } 299 300 /* look for the platform configuration file in the table */ 301 for (entry = NULL, i = 0; i < footer->num_table_entries; i++) { 302 if (table[i].type == HFI1_EFT_PLATFORM_CONFIG) { 303 entry = &table[i]; 304 break; 305 } 306 } 307 if (!entry) { 308 ret = -ENOENT; 309 goto done; 310 } 311 312 /* 313 * Sanity check on the configuration file size - it should never 314 * be larger than 4 KiB. 315 */ 316 if (entry->size > (4 * 1024)) { 317 dd_dev_err(dd, "Bad configuration file size 0x%x\n", 318 entry->size); 319 ret = -EINVAL; 320 goto done; 321 } 322 323 /* check for bogus offset and size that wrap when added together */ 324 if (entry->offset + entry->size < entry->offset) { 325 dd_dev_err(dd, 326 "Bad configuration file start + size 0x%x+0x%x\n", 327 entry->offset, entry->size); 328 ret = -EINVAL; 329 goto done; 330 } 331 332 /* allocate the buffer to return */ 333 buffer = kmalloc(entry->size, GFP_KERNEL); 334 if (!buffer) { 335 ret = -ENOMEM; 336 goto done; 337 } 338 339 /* 340 * Extract the file by looping over segments until it is fully read. 341 */ 342 seg_offset = entry->offset % SEG_SIZE; 343 seg_base = entry->offset - seg_offset; 344 ncopied = 0; 345 while (ncopied < entry->size) { 346 /* calculate data bytes available in this segment */ 347 348 /* start with the bytes from the current offset to the end */ 349 bytes_available = SEG_SIZE - seg_offset; 350 /* subtract off footer and table from segment 0 */ 351 if (seg_base == 0) { 352 /* 353 * Sanity check: should not have a starting point 354 * at or within the directory. 355 */ 356 if (bytes_available <= directory_size) { 357 dd_dev_err(dd, 358 "Bad configuration file - offset 0x%x within footer+table\n", 359 entry->offset); 360 ret = -EINVAL; 361 goto done; 362 } 363 bytes_available -= directory_size; 364 } 365 366 /* calculate bytes wanted */ 367 to_copy = entry->size - ncopied; 368 369 /* max out at the available bytes in this segment */ 370 if (to_copy > bytes_available) 371 to_copy = bytes_available; 372 373 /* 374 * Read from the EPROM. 375 * 376 * The sanity check for entry->offset is done in read_length(). 377 * The EPROM offset is validated against what the hardware 378 * addressing supports. In addition, if the offset is larger 379 * than the actual EPROM, it silently wraps. It will work 380 * fine, though the reader may not get what they expected 381 * from the EPROM. 382 */ 383 ret = read_length(dd, seg_base + seg_offset, to_copy, 384 buffer + ncopied); 385 if (ret) 386 goto done; 387 388 ncopied += to_copy; 389 390 /* set up for next segment */ 391 seg_offset = footer->oprom_size; 392 seg_base += SEG_SIZE; 393 } 394 395 /* success */ 396 ret = 0; 397 *data = buffer; 398 *size = entry->size; 399 400done: 401 kfree(table_buffer); 402 if (ret) 403 kfree(buffer); 404 return ret; 405} 406 407/* 408 * Read the platform configuration file from the EPROM. 409 * 410 * On success, an allocated buffer containing the data and its size are 411 * returned. It is up to the caller to free this buffer. 412 * 413 * Return value: 414 * 0 - success 415 * -ENXIO - no EPROM is available 416 * -EBUSY - not able to acquire access to the EPROM 417 * -ENOENT - no recognizable file written 418 * -ENOMEM - buffer could not be allocated 419 * -EINVAL - invalid EPROM contentents found 420 */ 421int eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size) 422{ 423 u32 directory[EP_PAGE_DWORDS]; /* aligned buffer */ 424 int ret; 425 426 if (!dd->eprom_available) 427 return -ENXIO; 428 429 ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); 430 if (ret) 431 return -EBUSY; 432 433 /* read the last page of the segment for the EPROM format magic */ 434 ret = read_length(dd, SEG_SIZE - EP_PAGE_SIZE, EP_PAGE_SIZE, directory); 435 if (ret) 436 goto done; 437 438 /* last dword of the segment contains a magic value */ 439 if (directory[EP_PAGE_DWORDS - 1] == FOOTER_MAGIC) { 440 /* segment format */ 441 ret = read_segment_platform_config(dd, directory, data, size); 442 } else { 443 /* partition format */ 444 ret = read_partition_platform_config(dd, data, size); 445 } 446 447done: 448 release_chip_resource(dd, CR_EPROM); 449 return ret; 450}