diff options
Diffstat (limited to 'arch/powerpc/kernel/rtas_flash.c')
-rw-r--r-- | arch/powerpc/kernel/rtas_flash.c | 452 |
1 files changed, 204 insertions, 248 deletions
diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index c642f0132988..5b770262c673 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c | |||
@@ -102,9 +102,10 @@ static struct kmem_cache *flash_block_cache = NULL; | |||
102 | 102 | ||
103 | #define FLASH_BLOCK_LIST_VERSION (1UL) | 103 | #define FLASH_BLOCK_LIST_VERSION (1UL) |
104 | 104 | ||
105 | /* Local copy of the flash block list. | 105 | /* |
106 | * We only allow one open of the flash proc file and create this | 106 | * Local copy of the flash block list. |
107 | * list as we go. The rtas_firmware_flash_list varable will be | 107 | * |
108 | * The rtas_firmware_flash_list varable will be | ||
108 | * set once the data is fully read. | 109 | * set once the data is fully read. |
109 | * | 110 | * |
110 | * For convenience as we build the list we use virtual addrs, | 111 | * For convenience as we build the list we use virtual addrs, |
@@ -125,23 +126,23 @@ struct rtas_update_flash_t | |||
125 | struct rtas_manage_flash_t | 126 | struct rtas_manage_flash_t |
126 | { | 127 | { |
127 | int status; /* Returned status */ | 128 | int status; /* Returned status */ |
128 | unsigned int op; /* Reject or commit image */ | ||
129 | }; | 129 | }; |
130 | 130 | ||
131 | /* Status int must be first member of struct */ | 131 | /* Status int must be first member of struct */ |
132 | struct rtas_validate_flash_t | 132 | struct rtas_validate_flash_t |
133 | { | 133 | { |
134 | int status; /* Returned status */ | 134 | int status; /* Returned status */ |
135 | char buf[VALIDATE_BUF_SIZE]; /* Candidate image buffer */ | 135 | char *buf; /* Candidate image buffer */ |
136 | unsigned int buf_size; /* Size of image buf */ | 136 | unsigned int buf_size; /* Size of image buf */ |
137 | unsigned int update_results; /* Update results token */ | 137 | unsigned int update_results; /* Update results token */ |
138 | }; | 138 | }; |
139 | 139 | ||
140 | static DEFINE_SPINLOCK(flash_file_open_lock); | 140 | static struct rtas_update_flash_t rtas_update_flash_data; |
141 | static struct proc_dir_entry *firmware_flash_pde; | 141 | static struct rtas_manage_flash_t rtas_manage_flash_data; |
142 | static struct proc_dir_entry *firmware_update_pde; | 142 | static struct rtas_validate_flash_t rtas_validate_flash_data; |
143 | static struct proc_dir_entry *validate_pde; | 143 | static DEFINE_MUTEX(rtas_update_flash_mutex); |
144 | static struct proc_dir_entry *manage_pde; | 144 | static DEFINE_MUTEX(rtas_manage_flash_mutex); |
145 | static DEFINE_MUTEX(rtas_validate_flash_mutex); | ||
145 | 146 | ||
146 | /* Do simple sanity checks on the flash image. */ | 147 | /* Do simple sanity checks on the flash image. */ |
147 | static int flash_list_valid(struct flash_block_list *flist) | 148 | static int flash_list_valid(struct flash_block_list *flist) |
@@ -191,10 +192,10 @@ static void free_flash_list(struct flash_block_list *f) | |||
191 | 192 | ||
192 | static int rtas_flash_release(struct inode *inode, struct file *file) | 193 | static int rtas_flash_release(struct inode *inode, struct file *file) |
193 | { | 194 | { |
194 | struct proc_dir_entry *dp = PDE(file_inode(file)); | 195 | struct rtas_update_flash_t *const uf = &rtas_update_flash_data; |
195 | struct rtas_update_flash_t *uf; | 196 | |
196 | 197 | mutex_lock(&rtas_update_flash_mutex); | |
197 | uf = (struct rtas_update_flash_t *) dp->data; | 198 | |
198 | if (uf->flist) { | 199 | if (uf->flist) { |
199 | /* File was opened in write mode for a new flash attempt */ | 200 | /* File was opened in write mode for a new flash attempt */ |
200 | /* Clear saved list */ | 201 | /* Clear saved list */ |
@@ -214,13 +215,14 @@ static int rtas_flash_release(struct inode *inode, struct file *file) | |||
214 | uf->flist = NULL; | 215 | uf->flist = NULL; |
215 | } | 216 | } |
216 | 217 | ||
217 | atomic_dec(&dp->count); | 218 | mutex_unlock(&rtas_update_flash_mutex); |
218 | return 0; | 219 | return 0; |
219 | } | 220 | } |
220 | 221 | ||
221 | static void get_flash_status_msg(int status, char *buf) | 222 | static size_t get_flash_status_msg(int status, char *buf) |
222 | { | 223 | { |
223 | char *msg; | 224 | const char *msg; |
225 | size_t len; | ||
224 | 226 | ||
225 | switch (status) { | 227 | switch (status) { |
226 | case FLASH_AUTH: | 228 | case FLASH_AUTH: |
@@ -242,34 +244,51 @@ static void get_flash_status_msg(int status, char *buf) | |||
242 | msg = "ready: firmware image ready for flash on reboot\n"; | 244 | msg = "ready: firmware image ready for flash on reboot\n"; |
243 | break; | 245 | break; |
244 | default: | 246 | default: |
245 | sprintf(buf, "error: unexpected status value %d\n", status); | 247 | return sprintf(buf, "error: unexpected status value %d\n", |
246 | return; | 248 | status); |
247 | } | 249 | } |
248 | 250 | ||
249 | strcpy(buf, msg); | 251 | len = strlen(msg); |
252 | memcpy(buf, msg, len + 1); | ||
253 | return len; | ||
250 | } | 254 | } |
251 | 255 | ||
252 | /* Reading the proc file will show status (not the firmware contents) */ | 256 | /* Reading the proc file will show status (not the firmware contents) */ |
253 | static ssize_t rtas_flash_read(struct file *file, char __user *buf, | 257 | static ssize_t rtas_flash_read_msg(struct file *file, char __user *buf, |
254 | size_t count, loff_t *ppos) | 258 | size_t count, loff_t *ppos) |
255 | { | 259 | { |
256 | struct proc_dir_entry *dp = PDE(file_inode(file)); | 260 | struct rtas_update_flash_t *const uf = &rtas_update_flash_data; |
257 | struct rtas_update_flash_t *uf; | ||
258 | char msg[RTAS_MSG_MAXLEN]; | 261 | char msg[RTAS_MSG_MAXLEN]; |
262 | size_t len; | ||
263 | int status; | ||
259 | 264 | ||
260 | uf = dp->data; | 265 | mutex_lock(&rtas_update_flash_mutex); |
266 | status = uf->status; | ||
267 | mutex_unlock(&rtas_update_flash_mutex); | ||
261 | 268 | ||
262 | if (!strcmp(dp->name, FIRMWARE_FLASH_NAME)) { | 269 | /* Read as text message */ |
263 | get_flash_status_msg(uf->status, msg); | 270 | len = get_flash_status_msg(status, msg); |
264 | } else { /* FIRMWARE_UPDATE_NAME */ | 271 | return simple_read_from_buffer(buf, count, ppos, msg, len); |
265 | sprintf(msg, "%d\n", uf->status); | 272 | } |
266 | } | 273 | |
274 | static ssize_t rtas_flash_read_num(struct file *file, char __user *buf, | ||
275 | size_t count, loff_t *ppos) | ||
276 | { | ||
277 | struct rtas_update_flash_t *const uf = &rtas_update_flash_data; | ||
278 | char msg[RTAS_MSG_MAXLEN]; | ||
279 | int status; | ||
267 | 280 | ||
281 | mutex_lock(&rtas_update_flash_mutex); | ||
282 | status = uf->status; | ||
283 | mutex_unlock(&rtas_update_flash_mutex); | ||
284 | |||
285 | /* Read as number */ | ||
286 | sprintf(msg, "%d\n", status); | ||
268 | return simple_read_from_buffer(buf, count, ppos, msg, strlen(msg)); | 287 | return simple_read_from_buffer(buf, count, ppos, msg, strlen(msg)); |
269 | } | 288 | } |
270 | 289 | ||
271 | /* constructor for flash_block_cache */ | 290 | /* constructor for flash_block_cache */ |
272 | void rtas_block_ctor(void *ptr) | 291 | static void rtas_block_ctor(void *ptr) |
273 | { | 292 | { |
274 | memset(ptr, 0, RTAS_BLK_SIZE); | 293 | memset(ptr, 0, RTAS_BLK_SIZE); |
275 | } | 294 | } |
@@ -282,16 +301,15 @@ void rtas_block_ctor(void *ptr) | |||
282 | static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, | 301 | static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, |
283 | size_t count, loff_t *off) | 302 | size_t count, loff_t *off) |
284 | { | 303 | { |
285 | struct proc_dir_entry *dp = PDE(file_inode(file)); | 304 | struct rtas_update_flash_t *const uf = &rtas_update_flash_data; |
286 | struct rtas_update_flash_t *uf; | ||
287 | char *p; | 305 | char *p; |
288 | int next_free; | 306 | int next_free, rc; |
289 | struct flash_block_list *fl; | 307 | struct flash_block_list *fl; |
290 | 308 | ||
291 | uf = (struct rtas_update_flash_t *) dp->data; | 309 | mutex_lock(&rtas_update_flash_mutex); |
292 | 310 | ||
293 | if (uf->status == FLASH_AUTH || count == 0) | 311 | if (uf->status == FLASH_AUTH || count == 0) |
294 | return count; /* discard data */ | 312 | goto out; /* discard data */ |
295 | 313 | ||
296 | /* In the case that the image is not ready for flashing, the memory | 314 | /* In the case that the image is not ready for flashing, the memory |
297 | * allocated for the block list will be freed upon the release of the | 315 | * allocated for the block list will be freed upon the release of the |
@@ -300,7 +318,7 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, | |||
300 | if (uf->flist == NULL) { | 318 | if (uf->flist == NULL) { |
301 | uf->flist = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); | 319 | uf->flist = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); |
302 | if (!uf->flist) | 320 | if (!uf->flist) |
303 | return -ENOMEM; | 321 | goto nomem; |
304 | } | 322 | } |
305 | 323 | ||
306 | fl = uf->flist; | 324 | fl = uf->flist; |
@@ -311,7 +329,7 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, | |||
311 | /* Need to allocate another block_list */ | 329 | /* Need to allocate another block_list */ |
312 | fl->next = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); | 330 | fl->next = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); |
313 | if (!fl->next) | 331 | if (!fl->next) |
314 | return -ENOMEM; | 332 | goto nomem; |
315 | fl = fl->next; | 333 | fl = fl->next; |
316 | next_free = 0; | 334 | next_free = 0; |
317 | } | 335 | } |
@@ -320,52 +338,37 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, | |||
320 | count = RTAS_BLK_SIZE; | 338 | count = RTAS_BLK_SIZE; |
321 | p = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); | 339 | p = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); |
322 | if (!p) | 340 | if (!p) |
323 | return -ENOMEM; | 341 | goto nomem; |
324 | 342 | ||
325 | if(copy_from_user(p, buffer, count)) { | 343 | if(copy_from_user(p, buffer, count)) { |
326 | kmem_cache_free(flash_block_cache, p); | 344 | kmem_cache_free(flash_block_cache, p); |
327 | return -EFAULT; | 345 | rc = -EFAULT; |
346 | goto error; | ||
328 | } | 347 | } |
329 | fl->blocks[next_free].data = p; | 348 | fl->blocks[next_free].data = p; |
330 | fl->blocks[next_free].length = count; | 349 | fl->blocks[next_free].length = count; |
331 | fl->num_blocks++; | 350 | fl->num_blocks++; |
332 | 351 | out: | |
352 | mutex_unlock(&rtas_update_flash_mutex); | ||
333 | return count; | 353 | return count; |
334 | } | ||
335 | |||
336 | static int rtas_excl_open(struct inode *inode, struct file *file) | ||
337 | { | ||
338 | struct proc_dir_entry *dp = PDE(inode); | ||
339 | |||
340 | /* Enforce exclusive open with use count of PDE */ | ||
341 | spin_lock(&flash_file_open_lock); | ||
342 | if (atomic_read(&dp->count) > 2) { | ||
343 | spin_unlock(&flash_file_open_lock); | ||
344 | return -EBUSY; | ||
345 | } | ||
346 | |||
347 | atomic_inc(&dp->count); | ||
348 | spin_unlock(&flash_file_open_lock); | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int rtas_excl_release(struct inode *inode, struct file *file) | ||
354 | { | ||
355 | struct proc_dir_entry *dp = PDE(inode); | ||
356 | 354 | ||
357 | atomic_dec(&dp->count); | 355 | nomem: |
358 | 356 | rc = -ENOMEM; | |
359 | return 0; | 357 | error: |
358 | mutex_unlock(&rtas_update_flash_mutex); | ||
359 | return rc; | ||
360 | } | 360 | } |
361 | 361 | ||
362 | static void manage_flash(struct rtas_manage_flash_t *args_buf) | 362 | /* |
363 | * Flash management routines. | ||
364 | */ | ||
365 | static void manage_flash(struct rtas_manage_flash_t *args_buf, unsigned int op) | ||
363 | { | 366 | { |
364 | s32 rc; | 367 | s32 rc; |
365 | 368 | ||
366 | do { | 369 | do { |
367 | rc = rtas_call(rtas_token("ibm,manage-flash-image"), 1, | 370 | rc = rtas_call(rtas_token("ibm,manage-flash-image"), 1, 1, |
368 | 1, NULL, args_buf->op); | 371 | NULL, op); |
369 | } while (rtas_busy_delay(rc)); | 372 | } while (rtas_busy_delay(rc)); |
370 | 373 | ||
371 | args_buf->status = rc; | 374 | args_buf->status = rc; |
@@ -374,55 +377,62 @@ static void manage_flash(struct rtas_manage_flash_t *args_buf) | |||
374 | static ssize_t manage_flash_read(struct file *file, char __user *buf, | 377 | static ssize_t manage_flash_read(struct file *file, char __user *buf, |
375 | size_t count, loff_t *ppos) | 378 | size_t count, loff_t *ppos) |
376 | { | 379 | { |
377 | struct proc_dir_entry *dp = PDE(file_inode(file)); | 380 | struct rtas_manage_flash_t *const args_buf = &rtas_manage_flash_data; |
378 | struct rtas_manage_flash_t *args_buf; | ||
379 | char msg[RTAS_MSG_MAXLEN]; | 381 | char msg[RTAS_MSG_MAXLEN]; |
380 | int msglen; | 382 | int msglen, status; |
381 | 383 | ||
382 | args_buf = dp->data; | 384 | mutex_lock(&rtas_manage_flash_mutex); |
383 | if (args_buf == NULL) | 385 | status = args_buf->status; |
384 | return 0; | 386 | mutex_unlock(&rtas_manage_flash_mutex); |
385 | |||
386 | msglen = sprintf(msg, "%d\n", args_buf->status); | ||
387 | 387 | ||
388 | msglen = sprintf(msg, "%d\n", status); | ||
388 | return simple_read_from_buffer(buf, count, ppos, msg, msglen); | 389 | return simple_read_from_buffer(buf, count, ppos, msg, msglen); |
389 | } | 390 | } |
390 | 391 | ||
391 | static ssize_t manage_flash_write(struct file *file, const char __user *buf, | 392 | static ssize_t manage_flash_write(struct file *file, const char __user *buf, |
392 | size_t count, loff_t *off) | 393 | size_t count, loff_t *off) |
393 | { | 394 | { |
394 | struct proc_dir_entry *dp = PDE(file_inode(file)); | 395 | struct rtas_manage_flash_t *const args_buf = &rtas_manage_flash_data; |
395 | struct rtas_manage_flash_t *args_buf; | 396 | static const char reject_str[] = "0"; |
396 | const char reject_str[] = "0"; | 397 | static const char commit_str[] = "1"; |
397 | const char commit_str[] = "1"; | ||
398 | char stkbuf[10]; | 398 | char stkbuf[10]; |
399 | int op; | 399 | int op, rc; |
400 | |||
401 | mutex_lock(&rtas_manage_flash_mutex); | ||
400 | 402 | ||
401 | args_buf = (struct rtas_manage_flash_t *) dp->data; | ||
402 | if ((args_buf->status == MANAGE_AUTH) || (count == 0)) | 403 | if ((args_buf->status == MANAGE_AUTH) || (count == 0)) |
403 | return count; | 404 | goto out; |
404 | 405 | ||
405 | op = -1; | 406 | op = -1; |
406 | if (buf) { | 407 | if (buf) { |
407 | if (count > 9) count = 9; | 408 | if (count > 9) count = 9; |
408 | if (copy_from_user (stkbuf, buf, count)) { | 409 | rc = -EFAULT; |
409 | return -EFAULT; | 410 | if (copy_from_user (stkbuf, buf, count)) |
410 | } | 411 | goto error; |
411 | if (strncmp(stkbuf, reject_str, strlen(reject_str)) == 0) | 412 | if (strncmp(stkbuf, reject_str, strlen(reject_str)) == 0) |
412 | op = RTAS_REJECT_TMP_IMG; | 413 | op = RTAS_REJECT_TMP_IMG; |
413 | else if (strncmp(stkbuf, commit_str, strlen(commit_str)) == 0) | 414 | else if (strncmp(stkbuf, commit_str, strlen(commit_str)) == 0) |
414 | op = RTAS_COMMIT_TMP_IMG; | 415 | op = RTAS_COMMIT_TMP_IMG; |
415 | } | 416 | } |
416 | 417 | ||
417 | if (op == -1) /* buf is empty, or contains invalid string */ | 418 | if (op == -1) { /* buf is empty, or contains invalid string */ |
418 | return -EINVAL; | 419 | rc = -EINVAL; |
419 | 420 | goto error; | |
420 | args_buf->op = op; | 421 | } |
421 | manage_flash(args_buf); | ||
422 | 422 | ||
423 | manage_flash(args_buf, op); | ||
424 | out: | ||
425 | mutex_unlock(&rtas_manage_flash_mutex); | ||
423 | return count; | 426 | return count; |
427 | |||
428 | error: | ||
429 | mutex_unlock(&rtas_manage_flash_mutex); | ||
430 | return rc; | ||
424 | } | 431 | } |
425 | 432 | ||
433 | /* | ||
434 | * Validation routines. | ||
435 | */ | ||
426 | static void validate_flash(struct rtas_validate_flash_t *args_buf) | 436 | static void validate_flash(struct rtas_validate_flash_t *args_buf) |
427 | { | 437 | { |
428 | int token = rtas_token("ibm,validate-flash-image"); | 438 | int token = rtas_token("ibm,validate-flash-image"); |
@@ -462,14 +472,14 @@ static int get_validate_flash_msg(struct rtas_validate_flash_t *args_buf, | |||
462 | static ssize_t validate_flash_read(struct file *file, char __user *buf, | 472 | static ssize_t validate_flash_read(struct file *file, char __user *buf, |
463 | size_t count, loff_t *ppos) | 473 | size_t count, loff_t *ppos) |
464 | { | 474 | { |
465 | struct proc_dir_entry *dp = PDE(file_inode(file)); | 475 | struct rtas_validate_flash_t *const args_buf = |
466 | struct rtas_validate_flash_t *args_buf; | 476 | &rtas_validate_flash_data; |
467 | char msg[RTAS_MSG_MAXLEN]; | 477 | char msg[RTAS_MSG_MAXLEN]; |
468 | int msglen; | 478 | int msglen; |
469 | 479 | ||
470 | args_buf = dp->data; | 480 | mutex_lock(&rtas_validate_flash_mutex); |
471 | |||
472 | msglen = get_validate_flash_msg(args_buf, msg); | 481 | msglen = get_validate_flash_msg(args_buf, msg); |
482 | mutex_unlock(&rtas_validate_flash_mutex); | ||
473 | 483 | ||
474 | return simple_read_from_buffer(buf, count, ppos, msg, msglen); | 484 | return simple_read_from_buffer(buf, count, ppos, msg, msglen); |
475 | } | 485 | } |
@@ -477,24 +487,18 @@ static ssize_t validate_flash_read(struct file *file, char __user *buf, | |||
477 | static ssize_t validate_flash_write(struct file *file, const char __user *buf, | 487 | static ssize_t validate_flash_write(struct file *file, const char __user *buf, |
478 | size_t count, loff_t *off) | 488 | size_t count, loff_t *off) |
479 | { | 489 | { |
480 | struct proc_dir_entry *dp = PDE(file_inode(file)); | 490 | struct rtas_validate_flash_t *const args_buf = |
481 | struct rtas_validate_flash_t *args_buf; | 491 | &rtas_validate_flash_data; |
482 | int rc; | 492 | int rc; |
483 | 493 | ||
484 | args_buf = (struct rtas_validate_flash_t *) dp->data; | 494 | mutex_lock(&rtas_validate_flash_mutex); |
485 | |||
486 | if (dp->data == NULL) { | ||
487 | dp->data = kmalloc(sizeof(struct rtas_validate_flash_t), | ||
488 | GFP_KERNEL); | ||
489 | if (dp->data == NULL) | ||
490 | return -ENOMEM; | ||
491 | } | ||
492 | 495 | ||
493 | /* We are only interested in the first 4K of the | 496 | /* We are only interested in the first 4K of the |
494 | * candidate image */ | 497 | * candidate image */ |
495 | if ((*off >= VALIDATE_BUF_SIZE) || | 498 | if ((*off >= VALIDATE_BUF_SIZE) || |
496 | (args_buf->status == VALIDATE_AUTH)) { | 499 | (args_buf->status == VALIDATE_AUTH)) { |
497 | *off += count; | 500 | *off += count; |
501 | mutex_unlock(&rtas_validate_flash_mutex); | ||
498 | return count; | 502 | return count; |
499 | } | 503 | } |
500 | 504 | ||
@@ -517,31 +521,29 @@ static ssize_t validate_flash_write(struct file *file, const char __user *buf, | |||
517 | *off += count; | 521 | *off += count; |
518 | rc = count; | 522 | rc = count; |
519 | done: | 523 | done: |
520 | if (rc < 0) { | 524 | mutex_unlock(&rtas_validate_flash_mutex); |
521 | kfree(dp->data); | ||
522 | dp->data = NULL; | ||
523 | } | ||
524 | return rc; | 525 | return rc; |
525 | } | 526 | } |
526 | 527 | ||
527 | static int validate_flash_release(struct inode *inode, struct file *file) | 528 | static int validate_flash_release(struct inode *inode, struct file *file) |
528 | { | 529 | { |
529 | struct proc_dir_entry *dp = PDE(file_inode(file)); | 530 | struct rtas_validate_flash_t *const args_buf = |
530 | struct rtas_validate_flash_t *args_buf; | 531 | &rtas_validate_flash_data; |
531 | 532 | ||
532 | args_buf = (struct rtas_validate_flash_t *) dp->data; | 533 | mutex_lock(&rtas_validate_flash_mutex); |
533 | 534 | ||
534 | if (args_buf->status == VALIDATE_READY) { | 535 | if (args_buf->status == VALIDATE_READY) { |
535 | args_buf->buf_size = VALIDATE_BUF_SIZE; | 536 | args_buf->buf_size = VALIDATE_BUF_SIZE; |
536 | validate_flash(args_buf); | 537 | validate_flash(args_buf); |
537 | } | 538 | } |
538 | 539 | ||
539 | /* The matching atomic_inc was in rtas_excl_open() */ | 540 | mutex_unlock(&rtas_validate_flash_mutex); |
540 | atomic_dec(&dp->count); | ||
541 | |||
542 | return 0; | 541 | return 0; |
543 | } | 542 | } |
544 | 543 | ||
544 | /* | ||
545 | * On-reboot flash update applicator. | ||
546 | */ | ||
545 | static void rtas_flash_firmware(int reboot_type) | 547 | static void rtas_flash_firmware(int reboot_type) |
546 | { | 548 | { |
547 | unsigned long image_size; | 549 | unsigned long image_size; |
@@ -634,75 +636,57 @@ static void rtas_flash_firmware(int reboot_type) | |||
634 | spin_unlock(&rtas_data_buf_lock); | 636 | spin_unlock(&rtas_data_buf_lock); |
635 | } | 637 | } |
636 | 638 | ||
637 | static void remove_flash_pde(struct proc_dir_entry *dp) | 639 | /* |
638 | { | 640 | * Manifest of proc files to create |
639 | if (dp) { | 641 | */ |
640 | kfree(dp->data); | 642 | struct rtas_flash_file { |
641 | remove_proc_entry(dp->name, dp->parent); | 643 | const char *filename; |
642 | } | 644 | const char *rtas_call_name; |
643 | } | ||
644 | |||
645 | static int initialize_flash_pde_data(const char *rtas_call_name, | ||
646 | size_t buf_size, | ||
647 | struct proc_dir_entry *dp) | ||
648 | { | ||
649 | int *status; | 645 | int *status; |
650 | int token; | 646 | const struct file_operations fops; |
651 | |||
652 | dp->data = kzalloc(buf_size, GFP_KERNEL); | ||
653 | if (dp->data == NULL) | ||
654 | return -ENOMEM; | ||
655 | |||
656 | /* | ||
657 | * This code assumes that the status int is the first member of the | ||
658 | * struct | ||
659 | */ | ||
660 | status = (int *) dp->data; | ||
661 | token = rtas_token(rtas_call_name); | ||
662 | if (token == RTAS_UNKNOWN_SERVICE) | ||
663 | *status = FLASH_AUTH; | ||
664 | else | ||
665 | *status = FLASH_NO_OP; | ||
666 | |||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | static struct proc_dir_entry *create_flash_pde(const char *filename, | ||
671 | const struct file_operations *fops) | ||
672 | { | ||
673 | return proc_create(filename, S_IRUSR | S_IWUSR, NULL, fops); | ||
674 | } | ||
675 | |||
676 | static const struct file_operations rtas_flash_operations = { | ||
677 | .owner = THIS_MODULE, | ||
678 | .read = rtas_flash_read, | ||
679 | .write = rtas_flash_write, | ||
680 | .open = rtas_excl_open, | ||
681 | .release = rtas_flash_release, | ||
682 | .llseek = default_llseek, | ||
683 | }; | ||
684 | |||
685 | static const struct file_operations manage_flash_operations = { | ||
686 | .owner = THIS_MODULE, | ||
687 | .read = manage_flash_read, | ||
688 | .write = manage_flash_write, | ||
689 | .open = rtas_excl_open, | ||
690 | .release = rtas_excl_release, | ||
691 | .llseek = default_llseek, | ||
692 | }; | 647 | }; |
693 | 648 | ||
694 | static const struct file_operations validate_flash_operations = { | 649 | static const struct rtas_flash_file rtas_flash_files[] = { |
695 | .owner = THIS_MODULE, | 650 | { |
696 | .read = validate_flash_read, | 651 | .filename = "powerpc/rtas/" FIRMWARE_FLASH_NAME, |
697 | .write = validate_flash_write, | 652 | .rtas_call_name = "ibm,update-flash-64-and-reboot", |
698 | .open = rtas_excl_open, | 653 | .status = &rtas_update_flash_data.status, |
699 | .release = validate_flash_release, | 654 | .fops.read = rtas_flash_read_msg, |
700 | .llseek = default_llseek, | 655 | .fops.write = rtas_flash_write, |
656 | .fops.release = rtas_flash_release, | ||
657 | .fops.llseek = default_llseek, | ||
658 | }, | ||
659 | { | ||
660 | .filename = "powerpc/rtas/" FIRMWARE_UPDATE_NAME, | ||
661 | .rtas_call_name = "ibm,update-flash-64-and-reboot", | ||
662 | .status = &rtas_update_flash_data.status, | ||
663 | .fops.read = rtas_flash_read_num, | ||
664 | .fops.write = rtas_flash_write, | ||
665 | .fops.release = rtas_flash_release, | ||
666 | .fops.llseek = default_llseek, | ||
667 | }, | ||
668 | { | ||
669 | .filename = "powerpc/rtas/" VALIDATE_FLASH_NAME, | ||
670 | .rtas_call_name = "ibm,validate-flash-image", | ||
671 | .status = &rtas_validate_flash_data.status, | ||
672 | .fops.read = validate_flash_read, | ||
673 | .fops.write = validate_flash_write, | ||
674 | .fops.release = validate_flash_release, | ||
675 | .fops.llseek = default_llseek, | ||
676 | }, | ||
677 | { | ||
678 | .filename = "powerpc/rtas/" MANAGE_FLASH_NAME, | ||
679 | .rtas_call_name = "ibm,manage-flash-image", | ||
680 | .status = &rtas_manage_flash_data.status, | ||
681 | .fops.read = manage_flash_read, | ||
682 | .fops.write = manage_flash_write, | ||
683 | .fops.llseek = default_llseek, | ||
684 | } | ||
701 | }; | 685 | }; |
702 | 686 | ||
703 | static int __init rtas_flash_init(void) | 687 | static int __init rtas_flash_init(void) |
704 | { | 688 | { |
705 | int rc; | 689 | int i; |
706 | 690 | ||
707 | if (rtas_token("ibm,update-flash-64-and-reboot") == | 691 | if (rtas_token("ibm,update-flash-64-and-reboot") == |
708 | RTAS_UNKNOWN_SERVICE) { | 692 | RTAS_UNKNOWN_SERVICE) { |
@@ -710,93 +694,65 @@ static int __init rtas_flash_init(void) | |||
710 | return 1; | 694 | return 1; |
711 | } | 695 | } |
712 | 696 | ||
713 | firmware_flash_pde = create_flash_pde("powerpc/rtas/" | 697 | rtas_validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); |
714 | FIRMWARE_FLASH_NAME, | 698 | if (!rtas_validate_flash_data.buf) |
715 | &rtas_flash_operations); | 699 | return -ENOMEM; |
716 | if (firmware_flash_pde == NULL) { | ||
717 | rc = -ENOMEM; | ||
718 | goto cleanup; | ||
719 | } | ||
720 | 700 | ||
721 | rc = initialize_flash_pde_data("ibm,update-flash-64-and-reboot", | 701 | flash_block_cache = kmem_cache_create("rtas_flash_cache", |
722 | sizeof(struct rtas_update_flash_t), | 702 | RTAS_BLK_SIZE, RTAS_BLK_SIZE, 0, |
723 | firmware_flash_pde); | 703 | rtas_block_ctor); |
724 | if (rc != 0) | 704 | if (!flash_block_cache) { |
725 | goto cleanup; | 705 | printk(KERN_ERR "%s: failed to create block cache\n", |
726 | 706 | __func__); | |
727 | firmware_update_pde = create_flash_pde("powerpc/rtas/" | 707 | goto enomem_buf; |
728 | FIRMWARE_UPDATE_NAME, | ||
729 | &rtas_flash_operations); | ||
730 | if (firmware_update_pde == NULL) { | ||
731 | rc = -ENOMEM; | ||
732 | goto cleanup; | ||
733 | } | 708 | } |
734 | 709 | ||
735 | rc = initialize_flash_pde_data("ibm,update-flash-64-and-reboot", | 710 | for (i = 0; i < ARRAY_SIZE(rtas_flash_files); i++) { |
736 | sizeof(struct rtas_update_flash_t), | 711 | const struct rtas_flash_file *f = &rtas_flash_files[i]; |
737 | firmware_update_pde); | 712 | int token; |
738 | if (rc != 0) | ||
739 | goto cleanup; | ||
740 | |||
741 | validate_pde = create_flash_pde("powerpc/rtas/" VALIDATE_FLASH_NAME, | ||
742 | &validate_flash_operations); | ||
743 | if (validate_pde == NULL) { | ||
744 | rc = -ENOMEM; | ||
745 | goto cleanup; | ||
746 | } | ||
747 | 713 | ||
748 | rc = initialize_flash_pde_data("ibm,validate-flash-image", | 714 | if (!proc_create(f->filename, S_IRUSR | S_IWUSR, NULL, &f->fops)) |
749 | sizeof(struct rtas_validate_flash_t), | 715 | goto enomem; |
750 | validate_pde); | ||
751 | if (rc != 0) | ||
752 | goto cleanup; | ||
753 | |||
754 | manage_pde = create_flash_pde("powerpc/rtas/" MANAGE_FLASH_NAME, | ||
755 | &manage_flash_operations); | ||
756 | if (manage_pde == NULL) { | ||
757 | rc = -ENOMEM; | ||
758 | goto cleanup; | ||
759 | } | ||
760 | 716 | ||
761 | rc = initialize_flash_pde_data("ibm,manage-flash-image", | 717 | /* |
762 | sizeof(struct rtas_manage_flash_t), | 718 | * This code assumes that the status int is the first member of the |
763 | manage_pde); | 719 | * struct |
764 | if (rc != 0) | 720 | */ |
765 | goto cleanup; | 721 | token = rtas_token(f->rtas_call_name); |
722 | if (token == RTAS_UNKNOWN_SERVICE) | ||
723 | *f->status = FLASH_AUTH; | ||
724 | else | ||
725 | *f->status = FLASH_NO_OP; | ||
726 | } | ||
766 | 727 | ||
767 | rtas_flash_term_hook = rtas_flash_firmware; | 728 | rtas_flash_term_hook = rtas_flash_firmware; |
768 | |||
769 | flash_block_cache = kmem_cache_create("rtas_flash_cache", | ||
770 | RTAS_BLK_SIZE, RTAS_BLK_SIZE, 0, | ||
771 | rtas_block_ctor); | ||
772 | if (!flash_block_cache) { | ||
773 | printk(KERN_ERR "%s: failed to create block cache\n", | ||
774 | __func__); | ||
775 | rc = -ENOMEM; | ||
776 | goto cleanup; | ||
777 | } | ||
778 | return 0; | 729 | return 0; |
779 | 730 | ||
780 | cleanup: | 731 | enomem: |
781 | remove_flash_pde(firmware_flash_pde); | 732 | while (--i >= 0) { |
782 | remove_flash_pde(firmware_update_pde); | 733 | const struct rtas_flash_file *f = &rtas_flash_files[i]; |
783 | remove_flash_pde(validate_pde); | 734 | remove_proc_entry(f->filename, NULL); |
784 | remove_flash_pde(manage_pde); | 735 | } |
785 | 736 | ||
786 | return rc; | 737 | kmem_cache_destroy(flash_block_cache); |
738 | enomem_buf: | ||
739 | kfree(rtas_validate_flash_data.buf); | ||
740 | return -ENOMEM; | ||
787 | } | 741 | } |
788 | 742 | ||
789 | static void __exit rtas_flash_cleanup(void) | 743 | static void __exit rtas_flash_cleanup(void) |
790 | { | 744 | { |
745 | int i; | ||
746 | |||
791 | rtas_flash_term_hook = NULL; | 747 | rtas_flash_term_hook = NULL; |
792 | 748 | ||
793 | if (flash_block_cache) | 749 | for (i = 0; i < ARRAY_SIZE(rtas_flash_files); i++) { |
794 | kmem_cache_destroy(flash_block_cache); | 750 | const struct rtas_flash_file *f = &rtas_flash_files[i]; |
751 | remove_proc_entry(f->filename, NULL); | ||
752 | } | ||
795 | 753 | ||
796 | remove_flash_pde(firmware_flash_pde); | 754 | kmem_cache_destroy(flash_block_cache); |
797 | remove_flash_pde(firmware_update_pde); | 755 | kfree(rtas_validate_flash_data.buf); |
798 | remove_flash_pde(validate_pde); | ||
799 | remove_flash_pde(manage_pde); | ||
800 | } | 756 | } |
801 | 757 | ||
802 | module_init(rtas_flash_init); | 758 | module_init(rtas_flash_init); |