diff options
Diffstat (limited to 'arch/powerpc/kernel/rtas_flash.c')
| -rw-r--r-- | arch/powerpc/kernel/rtas_flash.c | 47 |
1 files changed, 37 insertions, 10 deletions
diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 1442b63a75da..6f6fc977cb39 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c | |||
| @@ -72,6 +72,10 @@ | |||
| 72 | #define VALIDATE_BUF_SIZE 4096 | 72 | #define VALIDATE_BUF_SIZE 4096 |
| 73 | #define RTAS_MSG_MAXLEN 64 | 73 | #define RTAS_MSG_MAXLEN 64 |
| 74 | 74 | ||
| 75 | /* Quirk - RTAS requires 4k list length and block size */ | ||
| 76 | #define RTAS_BLKLIST_LENGTH 4096 | ||
| 77 | #define RTAS_BLK_SIZE 4096 | ||
| 78 | |||
| 75 | struct flash_block { | 79 | struct flash_block { |
| 76 | char *data; | 80 | char *data; |
| 77 | unsigned long length; | 81 | unsigned long length; |
| @@ -83,7 +87,7 @@ struct flash_block { | |||
| 83 | * into a version/length and translate the pointers | 87 | * into a version/length and translate the pointers |
| 84 | * to absolute. | 88 | * to absolute. |
| 85 | */ | 89 | */ |
| 86 | #define FLASH_BLOCKS_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct flash_block)) | 90 | #define FLASH_BLOCKS_PER_NODE ((RTAS_BLKLIST_LENGTH - 16) / sizeof(struct flash_block)) |
| 87 | struct flash_block_list { | 91 | struct flash_block_list { |
| 88 | unsigned long num_blocks; | 92 | unsigned long num_blocks; |
| 89 | struct flash_block_list *next; | 93 | struct flash_block_list *next; |
| @@ -96,6 +100,9 @@ struct flash_block_list_header { /* just the header of flash_block_list */ | |||
| 96 | 100 | ||
| 97 | static struct flash_block_list_header rtas_firmware_flash_list = {0, NULL}; | 101 | static struct flash_block_list_header rtas_firmware_flash_list = {0, NULL}; |
| 98 | 102 | ||
| 103 | /* Use slab cache to guarantee 4k alignment */ | ||
| 104 | static kmem_cache_t *flash_block_cache = NULL; | ||
| 105 | |||
| 99 | #define FLASH_BLOCK_LIST_VERSION (1UL) | 106 | #define FLASH_BLOCK_LIST_VERSION (1UL) |
| 100 | 107 | ||
| 101 | /* Local copy of the flash block list. | 108 | /* Local copy of the flash block list. |
| @@ -153,7 +160,7 @@ static int flash_list_valid(struct flash_block_list *flist) | |||
| 153 | return FLASH_IMG_NULL_DATA; | 160 | return FLASH_IMG_NULL_DATA; |
| 154 | } | 161 | } |
| 155 | block_size = f->blocks[i].length; | 162 | block_size = f->blocks[i].length; |
| 156 | if (block_size <= 0 || block_size > PAGE_SIZE) { | 163 | if (block_size <= 0 || block_size > RTAS_BLK_SIZE) { |
| 157 | return FLASH_IMG_BAD_LEN; | 164 | return FLASH_IMG_BAD_LEN; |
| 158 | } | 165 | } |
| 159 | image_size += block_size; | 166 | image_size += block_size; |
| @@ -177,9 +184,9 @@ static void free_flash_list(struct flash_block_list *f) | |||
| 177 | 184 | ||
| 178 | while (f) { | 185 | while (f) { |
| 179 | for (i = 0; i < f->num_blocks; i++) | 186 | for (i = 0; i < f->num_blocks; i++) |
| 180 | free_page((unsigned long)(f->blocks[i].data)); | 187 | kmem_cache_free(flash_block_cache, f->blocks[i].data); |
| 181 | next = f->next; | 188 | next = f->next; |
| 182 | free_page((unsigned long)f); | 189 | kmem_cache_free(flash_block_cache, f); |
| 183 | f = next; | 190 | f = next; |
| 184 | } | 191 | } |
| 185 | } | 192 | } |
| @@ -278,6 +285,12 @@ static ssize_t rtas_flash_read(struct file *file, char __user *buf, | |||
| 278 | return msglen; | 285 | return msglen; |
| 279 | } | 286 | } |
| 280 | 287 | ||
| 288 | /* constructor for flash_block_cache */ | ||
| 289 | void rtas_block_ctor(void *ptr, kmem_cache_t *cache, unsigned long flags) | ||
| 290 | { | ||
| 291 | memset(ptr, 0, RTAS_BLK_SIZE); | ||
| 292 | } | ||
| 293 | |||
| 281 | /* We could be much more efficient here. But to keep this function | 294 | /* We could be much more efficient here. But to keep this function |
| 282 | * simple we allocate a page to the block list no matter how small the | 295 | * simple we allocate a page to the block list no matter how small the |
| 283 | * count is. If the system is low on memory it will be just as well | 296 | * count is. If the system is low on memory it will be just as well |
| @@ -302,7 +315,7 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, | |||
| 302 | * proc file | 315 | * proc file |
| 303 | */ | 316 | */ |
| 304 | if (uf->flist == NULL) { | 317 | if (uf->flist == NULL) { |
| 305 | uf->flist = (struct flash_block_list *) get_zeroed_page(GFP_KERNEL); | 318 | uf->flist = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); |
| 306 | if (!uf->flist) | 319 | if (!uf->flist) |
| 307 | return -ENOMEM; | 320 | return -ENOMEM; |
| 308 | } | 321 | } |
| @@ -313,21 +326,21 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, | |||
| 313 | next_free = fl->num_blocks; | 326 | next_free = fl->num_blocks; |
| 314 | if (next_free == FLASH_BLOCKS_PER_NODE) { | 327 | if (next_free == FLASH_BLOCKS_PER_NODE) { |
| 315 | /* Need to allocate another block_list */ | 328 | /* Need to allocate another block_list */ |
| 316 | fl->next = (struct flash_block_list *)get_zeroed_page(GFP_KERNEL); | 329 | fl->next = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); |
| 317 | if (!fl->next) | 330 | if (!fl->next) |
| 318 | return -ENOMEM; | 331 | return -ENOMEM; |
| 319 | fl = fl->next; | 332 | fl = fl->next; |
| 320 | next_free = 0; | 333 | next_free = 0; |
| 321 | } | 334 | } |
| 322 | 335 | ||
| 323 | if (count > PAGE_SIZE) | 336 | if (count > RTAS_BLK_SIZE) |
| 324 | count = PAGE_SIZE; | 337 | count = RTAS_BLK_SIZE; |
| 325 | p = (char *)get_zeroed_page(GFP_KERNEL); | 338 | p = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); |
| 326 | if (!p) | 339 | if (!p) |
| 327 | return -ENOMEM; | 340 | return -ENOMEM; |
| 328 | 341 | ||
| 329 | if(copy_from_user(p, buffer, count)) { | 342 | if(copy_from_user(p, buffer, count)) { |
| 330 | free_page((unsigned long)p); | 343 | kmem_cache_free(flash_block_cache, p); |
| 331 | return -EFAULT; | 344 | return -EFAULT; |
| 332 | } | 345 | } |
| 333 | fl->blocks[next_free].data = p; | 346 | fl->blocks[next_free].data = p; |
| @@ -791,6 +804,16 @@ int __init rtas_flash_init(void) | |||
| 791 | goto cleanup; | 804 | goto cleanup; |
| 792 | 805 | ||
| 793 | rtas_flash_term_hook = rtas_flash_firmware; | 806 | rtas_flash_term_hook = rtas_flash_firmware; |
| 807 | |||
| 808 | flash_block_cache = kmem_cache_create("rtas_flash_cache", | ||
| 809 | RTAS_BLK_SIZE, RTAS_BLK_SIZE, 0, | ||
| 810 | rtas_block_ctor, NULL); | ||
| 811 | if (!flash_block_cache) { | ||
| 812 | printk(KERN_ERR "%s: failed to create block cache\n", | ||
| 813 | __FUNCTION__); | ||
| 814 | rc = -ENOMEM; | ||
| 815 | goto cleanup; | ||
| 816 | } | ||
| 794 | return 0; | 817 | return 0; |
| 795 | 818 | ||
| 796 | cleanup: | 819 | cleanup: |
| @@ -805,6 +828,10 @@ cleanup: | |||
| 805 | void __exit rtas_flash_cleanup(void) | 828 | void __exit rtas_flash_cleanup(void) |
| 806 | { | 829 | { |
| 807 | rtas_flash_term_hook = NULL; | 830 | rtas_flash_term_hook = NULL; |
| 831 | |||
| 832 | if (flash_block_cache) | ||
| 833 | kmem_cache_destroy(flash_block_cache); | ||
| 834 | |||
| 808 | remove_flash_pde(firmware_flash_pde); | 835 | remove_flash_pde(firmware_flash_pde); |
| 809 | remove_flash_pde(firmware_update_pde); | 836 | remove_flash_pde(firmware_update_pde); |
| 810 | remove_flash_pde(validate_pde); | 837 | remove_flash_pde(validate_pde); |
