diff options
author | Vasant Hegde <hegdevasant@linux.vnet.ibm.com> | 2013-10-24 06:34:58 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-10-30 01:12:02 -0400 |
commit | 50bd6153d1a68354a0a0c8bca1fe949fa8875875 (patch) | |
tree | 3bb5a63f30bfb162dbb17122ac0d6a3a51148adf | |
parent | 6f68b5e2c6c04e9cf0e3074f884da36957ce9aae (diff) |
powerpc/powernv: Code update interface
Code update interface for powernv platform. This provides
sysfs interface to pass new image, validate, update and
commit images.
This patch includes:
- Below OPAL APIs for code update
- opal_validate_flash()
- opal_manage_flash()
- opal_update_flash()
- Create below sysfs files under /sys/firmware/opal
- image : Interface to pass new FW image
- validate_flash : Validate candidate image
- manage_flash : Commit/Reject operations
- update_flash : Flash new candidate image
Updating Image:
"update_flash" is an interface to indicate flash new FW.
It just passes image SG list to FW. Actual flashing is done
during system reboot time.
Note:
- SG entry format:
I have kept version number to keep this list similar to what
PAPR is defined.
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | arch/powerpc/include/asm/opal.h | 7 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/Makefile | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-flash.c | 667 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-wrappers.S | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 4 |
5 files changed, 683 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index ee0efd2f2abc..033c06be1d84 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h | |||
@@ -129,6 +129,9 @@ extern int opal_enter_rtas(struct rtas_args *args, | |||
129 | #define OPAL_LPC_READ 67 | 129 | #define OPAL_LPC_READ 67 |
130 | #define OPAL_LPC_WRITE 68 | 130 | #define OPAL_LPC_WRITE 68 |
131 | #define OPAL_RETURN_CPU 69 | 131 | #define OPAL_RETURN_CPU 69 |
132 | #define OPAL_FLASH_VALIDATE 76 | ||
133 | #define OPAL_FLASH_MANAGE 77 | ||
134 | #define OPAL_FLASH_UPDATE 78 | ||
132 | 135 | ||
133 | #ifndef __ASSEMBLY__ | 136 | #ifndef __ASSEMBLY__ |
134 | 137 | ||
@@ -724,6 +727,9 @@ int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type, | |||
724 | uint32_t addr, uint32_t data, uint32_t sz); | 727 | uint32_t addr, uint32_t data, uint32_t sz); |
725 | int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type, | 728 | int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type, |
726 | uint32_t addr, uint32_t *data, uint32_t sz); | 729 | uint32_t addr, uint32_t *data, uint32_t sz); |
730 | int64_t opal_validate_flash(uint64_t buffer, uint32_t *size, uint32_t *result); | ||
731 | int64_t opal_manage_flash(uint8_t op); | ||
732 | int64_t opal_update_flash(uint64_t blk_list); | ||
727 | 733 | ||
728 | /* Internal functions */ | 734 | /* Internal functions */ |
729 | extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data); | 735 | extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data); |
@@ -752,6 +758,7 @@ extern int opal_set_rtc_time(struct rtc_time *tm); | |||
752 | extern void opal_get_rtc_time(struct rtc_time *tm); | 758 | extern void opal_get_rtc_time(struct rtc_time *tm); |
753 | extern unsigned long opal_get_boot_time(void); | 759 | extern unsigned long opal_get_boot_time(void); |
754 | extern void opal_nvram_init(void); | 760 | extern void opal_nvram_init(void); |
761 | extern void opal_flash_init(void); | ||
755 | 762 | ||
756 | extern int opal_machine_check(struct pt_regs *regs); | 763 | extern int opal_machine_check(struct pt_regs *regs); |
757 | 764 | ||
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 050d57e0c788..873fa1370dc4 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile | |||
@@ -1,5 +1,6 @@ | |||
1 | obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o | 1 | obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o |
2 | obj-y += opal-rtc.o opal-nvram.o opal-lpc.o rng.o | 2 | obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o |
3 | obj-y += rng.o | ||
3 | 4 | ||
4 | obj-$(CONFIG_SMP) += smp.o | 5 | obj-$(CONFIG_SMP) += smp.o |
5 | obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o | 6 | obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o |
diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c new file mode 100644 index 000000000000..6ffa6b1ec5b7 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-flash.c | |||
@@ -0,0 +1,667 @@ | |||
1 | /* | ||
2 | * PowerNV OPAL Firmware Update Interface | ||
3 | * | ||
4 | * Copyright 2013 IBM Corp. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #define DEBUG | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/reboot.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kobject.h> | ||
18 | #include <linux/sysfs.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/mm.h> | ||
21 | #include <linux/vmalloc.h> | ||
22 | #include <linux/pagemap.h> | ||
23 | |||
24 | #include <asm/opal.h> | ||
25 | |||
26 | /* FLASH status codes */ | ||
27 | #define FLASH_NO_OP -1099 /* No operation initiated by user */ | ||
28 | #define FLASH_NO_AUTH -9002 /* Not a service authority partition */ | ||
29 | |||
30 | /* Validate image status values */ | ||
31 | #define VALIDATE_IMG_READY -1001 /* Image ready for validation */ | ||
32 | #define VALIDATE_IMG_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */ | ||
33 | |||
34 | /* Manage image status values */ | ||
35 | #define MANAGE_ACTIVE_ERR -9001 /* Cannot overwrite active img */ | ||
36 | |||
37 | /* Flash image status values */ | ||
38 | #define FLASH_IMG_READY 0 /* Img ready for flash on reboot */ | ||
39 | #define FLASH_INVALID_IMG -1003 /* Flash image shorter than expected */ | ||
40 | #define FLASH_IMG_NULL_DATA -1004 /* Bad data in sg list entry */ | ||
41 | #define FLASH_IMG_BAD_LEN -1005 /* Bad length in sg list entry */ | ||
42 | |||
43 | /* Manage operation tokens */ | ||
44 | #define FLASH_REJECT_TMP_SIDE 0 /* Reject temporary fw image */ | ||
45 | #define FLASH_COMMIT_TMP_SIDE 1 /* Commit temporary fw image */ | ||
46 | |||
47 | /* Update tokens */ | ||
48 | #define FLASH_UPDATE_CANCEL 0 /* Cancel update request */ | ||
49 | #define FLASH_UPDATE_INIT 1 /* Initiate update */ | ||
50 | |||
51 | /* Validate image update result tokens */ | ||
52 | #define VALIDATE_TMP_UPDATE 0 /* T side will be updated */ | ||
53 | #define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */ | ||
54 | #define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */ | ||
55 | #define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */ | ||
56 | /* | ||
57 | * Current T side will be committed to P side before being replace with new | ||
58 | * image, and the new image is downlevel from current image | ||
59 | */ | ||
60 | #define VALIDATE_TMP_COMMIT_DL 4 | ||
61 | /* | ||
62 | * Current T side will be committed to P side before being replaced with new | ||
63 | * image | ||
64 | */ | ||
65 | #define VALIDATE_TMP_COMMIT 5 | ||
66 | /* | ||
67 | * T side will be updated with a downlevel image | ||
68 | */ | ||
69 | #define VALIDATE_TMP_UPDATE_DL 6 | ||
70 | /* | ||
71 | * The candidate image's release date is later than the system's firmware | ||
72 | * service entitlement date - service warranty period has expired | ||
73 | */ | ||
74 | #define VALIDATE_OUT_OF_WRNTY 7 | ||
75 | |||
76 | /* Validate buffer size */ | ||
77 | #define VALIDATE_BUF_SIZE 4096 | ||
78 | |||
79 | /* XXX: Assume candidate image size is <= 256MB */ | ||
80 | #define MAX_IMAGE_SIZE 0x10000000 | ||
81 | |||
82 | /* Flash sg list version */ | ||
83 | #define SG_LIST_VERSION (1UL) | ||
84 | |||
85 | /* Image status */ | ||
86 | enum { | ||
87 | IMAGE_INVALID, | ||
88 | IMAGE_LOADING, | ||
89 | IMAGE_READY, | ||
90 | }; | ||
91 | |||
92 | /* Candidate image data */ | ||
93 | struct image_data_t { | ||
94 | int status; | ||
95 | void *data; | ||
96 | uint32_t size; | ||
97 | }; | ||
98 | |||
99 | /* Candidate image header */ | ||
100 | struct image_header_t { | ||
101 | uint16_t magic; | ||
102 | uint16_t version; | ||
103 | uint32_t size; | ||
104 | }; | ||
105 | |||
106 | /* Scatter/gather entry */ | ||
107 | struct opal_sg_entry { | ||
108 | void *data; | ||
109 | long length; | ||
110 | }; | ||
111 | |||
112 | /* We calculate number of entries based on PAGE_SIZE */ | ||
113 | #define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry)) | ||
114 | |||
115 | /* | ||
116 | * This struct is very similar but not identical to that | ||
117 | * needed by the opal flash update. All we need to do for | ||
118 | * opal is rewrite num_entries into a version/length and | ||
119 | * translate the pointers to absolute. | ||
120 | */ | ||
121 | struct opal_sg_list { | ||
122 | unsigned long num_entries; | ||
123 | struct opal_sg_list *next; | ||
124 | struct opal_sg_entry entry[SG_ENTRIES_PER_NODE]; | ||
125 | }; | ||
126 | |||
127 | struct validate_flash_t { | ||
128 | int status; /* Return status */ | ||
129 | void *buf; /* Candiate image buffer */ | ||
130 | uint32_t buf_size; /* Image size */ | ||
131 | uint32_t result; /* Update results token */ | ||
132 | }; | ||
133 | |||
134 | struct manage_flash_t { | ||
135 | int status; /* Return status */ | ||
136 | }; | ||
137 | |||
138 | struct update_flash_t { | ||
139 | int status; /* Return status */ | ||
140 | }; | ||
141 | |||
142 | static struct image_header_t image_header; | ||
143 | static struct image_data_t image_data; | ||
144 | static struct validate_flash_t validate_flash_data; | ||
145 | static struct manage_flash_t manage_flash_data; | ||
146 | static struct update_flash_t update_flash_data; | ||
147 | |||
148 | static DEFINE_MUTEX(image_data_mutex); | ||
149 | |||
150 | /* | ||
151 | * Validate candidate image | ||
152 | */ | ||
153 | static inline void opal_flash_validate(void) | ||
154 | { | ||
155 | struct validate_flash_t *args_buf = &validate_flash_data; | ||
156 | |||
157 | args_buf->status = opal_validate_flash(__pa(args_buf->buf), | ||
158 | &(args_buf->buf_size), | ||
159 | &(args_buf->result)); | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * Validate output format: | ||
164 | * validate result token | ||
165 | * current image version details | ||
166 | * new image version details | ||
167 | */ | ||
168 | static ssize_t validate_show(struct kobject *kobj, | ||
169 | struct kobj_attribute *attr, char *buf) | ||
170 | { | ||
171 | struct validate_flash_t *args_buf = &validate_flash_data; | ||
172 | int len; | ||
173 | |||
174 | /* Candidate image is not validated */ | ||
175 | if (args_buf->status < VALIDATE_TMP_UPDATE) { | ||
176 | len = sprintf(buf, "%d\n", args_buf->status); | ||
177 | goto out; | ||
178 | } | ||
179 | |||
180 | /* Result token */ | ||
181 | len = sprintf(buf, "%d\n", args_buf->result); | ||
182 | |||
183 | /* Current and candidate image version details */ | ||
184 | if ((args_buf->result != VALIDATE_TMP_UPDATE) && | ||
185 | (args_buf->result < VALIDATE_CUR_UNKNOWN)) | ||
186 | goto out; | ||
187 | |||
188 | if (args_buf->buf_size > (VALIDATE_BUF_SIZE - len)) { | ||
189 | memcpy(buf + len, args_buf->buf, VALIDATE_BUF_SIZE - len); | ||
190 | len = VALIDATE_BUF_SIZE; | ||
191 | } else { | ||
192 | memcpy(buf + len, args_buf->buf, args_buf->buf_size); | ||
193 | len += args_buf->buf_size; | ||
194 | } | ||
195 | out: | ||
196 | /* Set status to default */ | ||
197 | args_buf->status = FLASH_NO_OP; | ||
198 | return len; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Validate candidate firmware image | ||
203 | * | ||
204 | * Note: | ||
205 | * We are only interested in first 4K bytes of the | ||
206 | * candidate image. | ||
207 | */ | ||
208 | static ssize_t validate_store(struct kobject *kobj, | ||
209 | struct kobj_attribute *attr, | ||
210 | const char *buf, size_t count) | ||
211 | { | ||
212 | struct validate_flash_t *args_buf = &validate_flash_data; | ||
213 | |||
214 | if (buf[0] != '1') | ||
215 | return -EINVAL; | ||
216 | |||
217 | mutex_lock(&image_data_mutex); | ||
218 | |||
219 | if (image_data.status != IMAGE_READY || | ||
220 | image_data.size < VALIDATE_BUF_SIZE) { | ||
221 | args_buf->result = VALIDATE_INVALID_IMG; | ||
222 | args_buf->status = VALIDATE_IMG_INCOMPLETE; | ||
223 | goto out; | ||
224 | } | ||
225 | |||
226 | /* Copy first 4k bytes of candidate image */ | ||
227 | memcpy(args_buf->buf, image_data.data, VALIDATE_BUF_SIZE); | ||
228 | |||
229 | args_buf->status = VALIDATE_IMG_READY; | ||
230 | args_buf->buf_size = VALIDATE_BUF_SIZE; | ||
231 | |||
232 | /* Validate candidate image */ | ||
233 | opal_flash_validate(); | ||
234 | |||
235 | out: | ||
236 | mutex_unlock(&image_data_mutex); | ||
237 | return count; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * Manage flash routine | ||
242 | */ | ||
243 | static inline void opal_flash_manage(uint8_t op) | ||
244 | { | ||
245 | struct manage_flash_t *const args_buf = &manage_flash_data; | ||
246 | |||
247 | args_buf->status = opal_manage_flash(op); | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Show manage flash status | ||
252 | */ | ||
253 | static ssize_t manage_show(struct kobject *kobj, | ||
254 | struct kobj_attribute *attr, char *buf) | ||
255 | { | ||
256 | struct manage_flash_t *const args_buf = &manage_flash_data; | ||
257 | int rc; | ||
258 | |||
259 | rc = sprintf(buf, "%d\n", args_buf->status); | ||
260 | /* Set status to default*/ | ||
261 | args_buf->status = FLASH_NO_OP; | ||
262 | return rc; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Manage operations: | ||
267 | * 0 - Reject | ||
268 | * 1 - Commit | ||
269 | */ | ||
270 | static ssize_t manage_store(struct kobject *kobj, | ||
271 | struct kobj_attribute *attr, | ||
272 | const char *buf, size_t count) | ||
273 | { | ||
274 | uint8_t op; | ||
275 | switch (buf[0]) { | ||
276 | case '0': | ||
277 | op = FLASH_REJECT_TMP_SIDE; | ||
278 | break; | ||
279 | case '1': | ||
280 | op = FLASH_COMMIT_TMP_SIDE; | ||
281 | break; | ||
282 | default: | ||
283 | return -EINVAL; | ||
284 | } | ||
285 | |||
286 | /* commit/reject temporary image */ | ||
287 | opal_flash_manage(op); | ||
288 | return count; | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * Free sg list | ||
293 | */ | ||
294 | static void free_sg_list(struct opal_sg_list *list) | ||
295 | { | ||
296 | struct opal_sg_list *sg1; | ||
297 | while (list) { | ||
298 | sg1 = list->next; | ||
299 | kfree(list); | ||
300 | list = sg1; | ||
301 | } | ||
302 | list = NULL; | ||
303 | } | ||
304 | |||
305 | /* | ||
306 | * Build candidate image scatter gather list | ||
307 | * | ||
308 | * list format: | ||
309 | * ----------------------------------- | ||
310 | * | VER (8) | Entry length in bytes | | ||
311 | * ----------------------------------- | ||
312 | * | Pointer to next entry | | ||
313 | * ----------------------------------- | ||
314 | * | Address of memory area 1 | | ||
315 | * ----------------------------------- | ||
316 | * | Length of memory area 1 | | ||
317 | * ----------------------------------- | ||
318 | * | ......... | | ||
319 | * ----------------------------------- | ||
320 | * | ......... | | ||
321 | * ----------------------------------- | ||
322 | * | Address of memory area N | | ||
323 | * ----------------------------------- | ||
324 | * | Length of memory area N | | ||
325 | * ----------------------------------- | ||
326 | */ | ||
327 | static struct opal_sg_list *image_data_to_sglist(void) | ||
328 | { | ||
329 | struct opal_sg_list *sg1, *list = NULL; | ||
330 | void *addr; | ||
331 | int size; | ||
332 | |||
333 | addr = image_data.data; | ||
334 | size = image_data.size; | ||
335 | |||
336 | sg1 = kzalloc((sizeof(struct opal_sg_list)), GFP_KERNEL); | ||
337 | if (!sg1) | ||
338 | return NULL; | ||
339 | |||
340 | list = sg1; | ||
341 | sg1->num_entries = 0; | ||
342 | while (size > 0) { | ||
343 | /* Translate virtual address to physical address */ | ||
344 | sg1->entry[sg1->num_entries].data = | ||
345 | (void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT); | ||
346 | |||
347 | if (size > PAGE_SIZE) | ||
348 | sg1->entry[sg1->num_entries].length = PAGE_SIZE; | ||
349 | else | ||
350 | sg1->entry[sg1->num_entries].length = size; | ||
351 | |||
352 | sg1->num_entries++; | ||
353 | if (sg1->num_entries >= SG_ENTRIES_PER_NODE) { | ||
354 | sg1->next = kzalloc((sizeof(struct opal_sg_list)), | ||
355 | GFP_KERNEL); | ||
356 | if (!sg1->next) { | ||
357 | pr_err("%s : Failed to allocate memory\n", | ||
358 | __func__); | ||
359 | goto nomem; | ||
360 | } | ||
361 | |||
362 | sg1 = sg1->next; | ||
363 | sg1->num_entries = 0; | ||
364 | } | ||
365 | addr += PAGE_SIZE; | ||
366 | size -= PAGE_SIZE; | ||
367 | } | ||
368 | return list; | ||
369 | nomem: | ||
370 | free_sg_list(list); | ||
371 | return NULL; | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * OPAL update flash | ||
376 | */ | ||
377 | static int opal_flash_update(int op) | ||
378 | { | ||
379 | struct opal_sg_list *sg, *list, *next; | ||
380 | unsigned long addr; | ||
381 | int64_t rc = OPAL_PARAMETER; | ||
382 | |||
383 | if (op == FLASH_UPDATE_CANCEL) { | ||
384 | pr_alert("FLASH: Image update cancelled\n"); | ||
385 | addr = '\0'; | ||
386 | goto flash; | ||
387 | } | ||
388 | |||
389 | list = image_data_to_sglist(); | ||
390 | if (!list) | ||
391 | goto invalid_img; | ||
392 | |||
393 | /* First entry address */ | ||
394 | addr = __pa(list); | ||
395 | |||
396 | /* Translate sg list address to absolute */ | ||
397 | for (sg = list; sg; sg = next) { | ||
398 | next = sg->next; | ||
399 | /* Don't translate NULL pointer for last entry */ | ||
400 | if (sg->next) | ||
401 | sg->next = (struct opal_sg_list *)__pa(sg->next); | ||
402 | else | ||
403 | sg->next = NULL; | ||
404 | |||
405 | /* Make num_entries into the version/length field */ | ||
406 | sg->num_entries = (SG_LIST_VERSION << 56) | | ||
407 | (sg->num_entries * sizeof(struct opal_sg_entry) + 16); | ||
408 | } | ||
409 | |||
410 | pr_alert("FLASH: Image is %u bytes\n", image_data.size); | ||
411 | pr_alert("FLASH: Image update requested\n"); | ||
412 | pr_alert("FLASH: Image will be updated during system reboot\n"); | ||
413 | pr_alert("FLASH: This will take several minutes. Do not power off!\n"); | ||
414 | |||
415 | flash: | ||
416 | rc = opal_update_flash(addr); | ||
417 | |||
418 | invalid_img: | ||
419 | return rc; | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * Show candidate image status | ||
424 | */ | ||
425 | static ssize_t update_show(struct kobject *kobj, | ||
426 | struct kobj_attribute *attr, char *buf) | ||
427 | { | ||
428 | struct update_flash_t *const args_buf = &update_flash_data; | ||
429 | return sprintf(buf, "%d\n", args_buf->status); | ||
430 | } | ||
431 | |||
432 | /* | ||
433 | * Set update image flag | ||
434 | * 1 - Flash new image | ||
435 | * 0 - Cancel flash request | ||
436 | */ | ||
437 | static ssize_t update_store(struct kobject *kobj, | ||
438 | struct kobj_attribute *attr, | ||
439 | const char *buf, size_t count) | ||
440 | { | ||
441 | struct update_flash_t *const args_buf = &update_flash_data; | ||
442 | int rc = count; | ||
443 | |||
444 | mutex_lock(&image_data_mutex); | ||
445 | |||
446 | switch (buf[0]) { | ||
447 | case '0': | ||
448 | if (args_buf->status == FLASH_IMG_READY) | ||
449 | opal_flash_update(FLASH_UPDATE_CANCEL); | ||
450 | args_buf->status = FLASH_NO_OP; | ||
451 | break; | ||
452 | case '1': | ||
453 | /* Image is loaded? */ | ||
454 | if (image_data.status == IMAGE_READY) | ||
455 | args_buf->status = | ||
456 | opal_flash_update(FLASH_UPDATE_INIT); | ||
457 | else | ||
458 | args_buf->status = FLASH_INVALID_IMG; | ||
459 | break; | ||
460 | default: | ||
461 | rc = -EINVAL; | ||
462 | } | ||
463 | |||
464 | mutex_unlock(&image_data_mutex); | ||
465 | return rc; | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | * Free image buffer | ||
470 | */ | ||
471 | static void free_image_buf(void) | ||
472 | { | ||
473 | void *addr; | ||
474 | int size; | ||
475 | |||
476 | addr = image_data.data; | ||
477 | size = PAGE_ALIGN(image_data.size); | ||
478 | while (size > 0) { | ||
479 | ClearPageReserved(vmalloc_to_page(addr)); | ||
480 | addr += PAGE_SIZE; | ||
481 | size -= PAGE_SIZE; | ||
482 | } | ||
483 | vfree(image_data.data); | ||
484 | image_data.data = NULL; | ||
485 | image_data.status = IMAGE_INVALID; | ||
486 | } | ||
487 | |||
488 | /* | ||
489 | * Allocate image buffer. | ||
490 | */ | ||
491 | static int alloc_image_buf(char *buffer, size_t count) | ||
492 | { | ||
493 | void *addr; | ||
494 | int size; | ||
495 | |||
496 | if (count < sizeof(struct image_header_t)) { | ||
497 | pr_warn("FLASH: Invalid candidate image\n"); | ||
498 | return -EINVAL; | ||
499 | } | ||
500 | |||
501 | memcpy(&image_header, (void *)buffer, sizeof(struct image_header_t)); | ||
502 | image_data.size = be32_to_cpu(image_header.size); | ||
503 | pr_debug("FLASH: Candiate image size = %u\n", image_data.size); | ||
504 | |||
505 | if (image_data.size > MAX_IMAGE_SIZE) { | ||
506 | pr_warn("FLASH: Too large image\n"); | ||
507 | return -EINVAL; | ||
508 | } | ||
509 | if (image_data.size < VALIDATE_BUF_SIZE) { | ||
510 | pr_warn("FLASH: Image is shorter than expected\n"); | ||
511 | return -EINVAL; | ||
512 | } | ||
513 | |||
514 | image_data.data = vzalloc(PAGE_ALIGN(image_data.size)); | ||
515 | if (!image_data.data) { | ||
516 | pr_err("%s : Failed to allocate memory\n", __func__); | ||
517 | return -ENOMEM; | ||
518 | } | ||
519 | |||
520 | /* Pin memory */ | ||
521 | addr = image_data.data; | ||
522 | size = PAGE_ALIGN(image_data.size); | ||
523 | while (size > 0) { | ||
524 | SetPageReserved(vmalloc_to_page(addr)); | ||
525 | addr += PAGE_SIZE; | ||
526 | size -= PAGE_SIZE; | ||
527 | } | ||
528 | |||
529 | image_data.status = IMAGE_LOADING; | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * Copy candidate image | ||
535 | * | ||
536 | * Parse candidate image header to get total image size | ||
537 | * and pre-allocate required memory. | ||
538 | */ | ||
539 | static ssize_t image_data_write(struct file *filp, struct kobject *kobj, | ||
540 | struct bin_attribute *bin_attr, | ||
541 | char *buffer, loff_t pos, size_t count) | ||
542 | { | ||
543 | int rc; | ||
544 | |||
545 | mutex_lock(&image_data_mutex); | ||
546 | |||
547 | /* New image ? */ | ||
548 | if (pos == 0) { | ||
549 | /* Free memory, if already allocated */ | ||
550 | if (image_data.data) | ||
551 | free_image_buf(); | ||
552 | |||
553 | /* Cancel outstanding image update request */ | ||
554 | if (update_flash_data.status == FLASH_IMG_READY) | ||
555 | opal_flash_update(FLASH_UPDATE_CANCEL); | ||
556 | |||
557 | /* Allocate memory */ | ||
558 | rc = alloc_image_buf(buffer, count); | ||
559 | if (rc) | ||
560 | goto out; | ||
561 | } | ||
562 | |||
563 | if (image_data.status != IMAGE_LOADING) { | ||
564 | rc = -ENOMEM; | ||
565 | goto out; | ||
566 | } | ||
567 | |||
568 | if ((pos + count) > image_data.size) { | ||
569 | rc = -EINVAL; | ||
570 | goto out; | ||
571 | } | ||
572 | |||
573 | memcpy(image_data.data + pos, (void *)buffer, count); | ||
574 | rc = count; | ||
575 | |||
576 | /* Set image status */ | ||
577 | if ((pos + count) == image_data.size) { | ||
578 | pr_debug("FLASH: Candidate image loaded....\n"); | ||
579 | image_data.status = IMAGE_READY; | ||
580 | } | ||
581 | |||
582 | out: | ||
583 | mutex_unlock(&image_data_mutex); | ||
584 | return rc; | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * sysfs interface : | ||
589 | * OPAL uses below sysfs files for code update. | ||
590 | * We create these files under /sys/firmware/opal. | ||
591 | * | ||
592 | * image : Interface to load candidate firmware image | ||
593 | * validate_flash : Validate firmware image | ||
594 | * manage_flash : Commit/Reject firmware image | ||
595 | * update_flash : Flash new firmware image | ||
596 | * | ||
597 | */ | ||
598 | static struct bin_attribute image_data_attr = { | ||
599 | .attr = {.name = "image", .mode = 0200}, | ||
600 | .size = MAX_IMAGE_SIZE, /* Limit image size */ | ||
601 | .write = image_data_write, | ||
602 | }; | ||
603 | |||
604 | static struct kobj_attribute validate_attribute = | ||
605 | __ATTR(validate_flash, 0600, validate_show, validate_store); | ||
606 | |||
607 | static struct kobj_attribute manage_attribute = | ||
608 | __ATTR(manage_flash, 0600, manage_show, manage_store); | ||
609 | |||
610 | static struct kobj_attribute update_attribute = | ||
611 | __ATTR(update_flash, 0600, update_show, update_store); | ||
612 | |||
613 | static struct attribute *image_op_attrs[] = { | ||
614 | &validate_attribute.attr, | ||
615 | &manage_attribute.attr, | ||
616 | &update_attribute.attr, | ||
617 | NULL /* need to NULL terminate the list of attributes */ | ||
618 | }; | ||
619 | |||
620 | static struct attribute_group image_op_attr_group = { | ||
621 | .attrs = image_op_attrs, | ||
622 | }; | ||
623 | |||
624 | void __init opal_flash_init(void) | ||
625 | { | ||
626 | int ret; | ||
627 | |||
628 | /* Allocate validate image buffer */ | ||
629 | validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); | ||
630 | if (!validate_flash_data.buf) { | ||
631 | pr_err("%s : Failed to allocate memory\n", __func__); | ||
632 | return; | ||
633 | } | ||
634 | |||
635 | /* Make sure /sys/firmware/opal directory is created */ | ||
636 | if (!opal_kobj) { | ||
637 | pr_warn("FLASH: opal kobject is not available\n"); | ||
638 | goto nokobj; | ||
639 | } | ||
640 | |||
641 | /* Create the sysfs files */ | ||
642 | ret = sysfs_create_group(opal_kobj, &image_op_attr_group); | ||
643 | if (ret) { | ||
644 | pr_warn("FLASH: Failed to create sysfs files\n"); | ||
645 | goto nokobj; | ||
646 | } | ||
647 | |||
648 | ret = sysfs_create_bin_file(opal_kobj, &image_data_attr); | ||
649 | if (ret) { | ||
650 | pr_warn("FLASH: Failed to create sysfs files\n"); | ||
651 | goto nosysfs_file; | ||
652 | } | ||
653 | |||
654 | /* Set default status */ | ||
655 | validate_flash_data.status = FLASH_NO_OP; | ||
656 | manage_flash_data.status = FLASH_NO_OP; | ||
657 | update_flash_data.status = FLASH_NO_OP; | ||
658 | image_data.status = IMAGE_INVALID; | ||
659 | return; | ||
660 | |||
661 | nosysfs_file: | ||
662 | sysfs_remove_group(opal_kobj, &image_op_attr_group); | ||
663 | |||
664 | nokobj: | ||
665 | kfree(validate_flash_data.buf); | ||
666 | return; | ||
667 | } | ||
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S index 403d05840625..e7806504e976 100644 --- a/arch/powerpc/platforms/powernv/opal-wrappers.S +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S | |||
@@ -123,3 +123,6 @@ OPAL_CALL(opal_xscom_write, OPAL_XSCOM_WRITE); | |||
123 | OPAL_CALL(opal_lpc_read, OPAL_LPC_READ); | 123 | OPAL_CALL(opal_lpc_read, OPAL_LPC_READ); |
124 | OPAL_CALL(opal_lpc_write, OPAL_LPC_WRITE); | 124 | OPAL_CALL(opal_lpc_write, OPAL_LPC_WRITE); |
125 | OPAL_CALL(opal_return_cpu, OPAL_RETURN_CPU); | 125 | OPAL_CALL(opal_return_cpu, OPAL_RETURN_CPU); |
126 | OPAL_CALL(opal_validate_flash, OPAL_FLASH_VALIDATE); | ||
127 | OPAL_CALL(opal_manage_flash, OPAL_FLASH_MANAGE); | ||
128 | OPAL_CALL(opal_update_flash, OPAL_FLASH_UPDATE); | ||
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 37f06580709a..b56c243aaee9 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c | |||
@@ -438,6 +438,10 @@ static int __init opal_init(void) | |||
438 | 438 | ||
439 | /* Create "opal" kobject under /sys/firmware */ | 439 | /* Create "opal" kobject under /sys/firmware */ |
440 | rc = opal_sysfs_init(); | 440 | rc = opal_sysfs_init(); |
441 | if (rc == 0) { | ||
442 | /* Setup code update interface */ | ||
443 | opal_flash_init(); | ||
444 | } | ||
441 | 445 | ||
442 | return 0; | 446 | return 0; |
443 | } | 447 | } |