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 | */ |
47 | static 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 | */ |
60 | static 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, offset: read_start, result: 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, offset: start, result: 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, offset: start, result: buffer); |
120 | memcpy(dest, buffer, len); |
121 | } |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | /* |
127 | * Initialize the EPROM handler. |
128 | */ |
129 | int 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); |
162 | done_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 | |
178 | struct { |
179 | u32 ; /* size of the oprom, in bytes */ |
180 | u16 ; |
181 | u16 ; /* version of this footer */ |
182 | u32 ; /* must be last */ |
183 | }; |
184 | |
185 | struct 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 MAGIC4('e', 'p', 'r', 'm') |
204 | #define 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 | */ |
210 | static 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, dest: buffer); |
223 | if (ret) { |
224 | kfree(objp: buffer); |
225 | return ret; |
226 | } |
227 | |
228 | /* config partition is valid only if it starts with IMAGE_START_MAGIC */ |
229 | if (memcmp(p: buffer, IMAGE_START_MAGIC, strlen(IMAGE_START_MAGIC))) { |
230 | kfree(objp: 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 | */ |
252 | static int read_segment_platform_config(struct hfi1_devdata *dd, |
253 | void *directory, void **data, u32 *size) |
254 | { |
255 | struct hfi1_eprom_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(size: directory_size, GFP_KERNEL); |
291 | if (!table_buffer) |
292 | return -ENOMEM; |
293 | ret = read_length(dd, SEG_SIZE - directory_size, |
294 | len: directory_size, dest: 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(size: 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, start: seg_base + seg_offset, len: to_copy, |
384 | dest: 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 | |
400 | done: |
401 | kfree(objp: table_buffer); |
402 | if (ret) |
403 | kfree(objp: 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 | */ |
421 | int 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, dest: 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 | |
447 | done: |
448 | release_chip_resource(dd, CR_EPROM); |
449 | return ret; |
450 | } |
451 | |