12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2f4fcbbe9SPaul Mackerras /* 3f4fcbbe9SPaul Mackerras * c 2001 PPC 64 Team, IBM Corp 4f4fcbbe9SPaul Mackerras * 5188917e1SBenjamin Herrenschmidt * /proc/powerpc/rtas/firmware_flash interface 6f4fcbbe9SPaul Mackerras * 7f4fcbbe9SPaul Mackerras * This file implements a firmware_flash interface to pump a firmware 8f4fcbbe9SPaul Mackerras * image into the kernel. At reboot time rtas_restart() will see the 9f4fcbbe9SPaul Mackerras * firmware image and flash it as it reboots (see rtas.c). 10f4fcbbe9SPaul Mackerras */ 11f4fcbbe9SPaul Mackerras 12f4fcbbe9SPaul Mackerras #include <linux/module.h> 13f4fcbbe9SPaul Mackerras #include <linux/init.h> 145a0e3ad6STejun Heo #include <linux/slab.h> 15f4fcbbe9SPaul Mackerras #include <linux/proc_fs.h> 16c5f41752SAmerigo Wang #include <linux/reboot.h> 17f4fcbbe9SPaul Mackerras #include <asm/delay.h> 187c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 19f4fcbbe9SPaul Mackerras #include <asm/rtas.h> 20f4fcbbe9SPaul Mackerras 21f4fcbbe9SPaul Mackerras #define MODULE_VERS "1.0" 22f4fcbbe9SPaul Mackerras #define MODULE_NAME "rtas_flash" 23f4fcbbe9SPaul Mackerras 24f4fcbbe9SPaul Mackerras #define FIRMWARE_FLASH_NAME "firmware_flash" 25f4fcbbe9SPaul Mackerras #define FIRMWARE_UPDATE_NAME "firmware_update" 26f4fcbbe9SPaul Mackerras #define MANAGE_FLASH_NAME "manage_flash" 27f4fcbbe9SPaul Mackerras #define VALIDATE_FLASH_NAME "validate_flash" 28f4fcbbe9SPaul Mackerras 29f4fcbbe9SPaul Mackerras /* General RTAS Status Codes */ 30f4fcbbe9SPaul Mackerras #define RTAS_RC_SUCCESS 0 31f4fcbbe9SPaul Mackerras #define RTAS_RC_HW_ERR -1 32f4fcbbe9SPaul Mackerras #define RTAS_RC_BUSY -2 33f4fcbbe9SPaul Mackerras 34f4fcbbe9SPaul Mackerras /* Flash image status values */ 35f4fcbbe9SPaul Mackerras #define FLASH_AUTH -9002 /* RTAS Not Service Authority Partition */ 36f4fcbbe9SPaul Mackerras #define FLASH_NO_OP -1099 /* No operation initiated by user */ 37f4fcbbe9SPaul Mackerras #define FLASH_IMG_SHORT -1005 /* Flash image shorter than expected */ 38f4fcbbe9SPaul Mackerras #define FLASH_IMG_BAD_LEN -1004 /* Bad length value in flash list block */ 39f4fcbbe9SPaul Mackerras #define FLASH_IMG_NULL_DATA -1003 /* Bad data value in flash list block */ 40f4fcbbe9SPaul Mackerras #define FLASH_IMG_READY 0 /* Firmware img ready for flash on reboot */ 41f4fcbbe9SPaul Mackerras 42f4fcbbe9SPaul Mackerras /* Manage image status values */ 43f4fcbbe9SPaul Mackerras #define MANAGE_AUTH -9002 /* RTAS Not Service Authority Partition */ 44f4fcbbe9SPaul Mackerras #define MANAGE_ACTIVE_ERR -9001 /* RTAS Cannot Overwrite Active Img */ 45f4fcbbe9SPaul Mackerras #define MANAGE_NO_OP -1099 /* No operation initiated by user */ 46f4fcbbe9SPaul Mackerras #define MANAGE_PARAM_ERR -3 /* RTAS Parameter Error */ 47f4fcbbe9SPaul Mackerras #define MANAGE_HW_ERR -1 /* RTAS Hardware Error */ 48f4fcbbe9SPaul Mackerras 49f4fcbbe9SPaul Mackerras /* Validate image status values */ 50f4fcbbe9SPaul Mackerras #define VALIDATE_AUTH -9002 /* RTAS Not Service Authority Partition */ 51f4fcbbe9SPaul Mackerras #define VALIDATE_NO_OP -1099 /* No operation initiated by the user */ 52f4fcbbe9SPaul Mackerras #define VALIDATE_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */ 53f4fcbbe9SPaul Mackerras #define VALIDATE_READY -1001 /* Firmware image ready for validation */ 54f4fcbbe9SPaul Mackerras #define VALIDATE_PARAM_ERR -3 /* RTAS Parameter Error */ 55f4fcbbe9SPaul Mackerras #define VALIDATE_HW_ERR -1 /* RTAS Hardware Error */ 56f51005d7SVasant Hegde 57f51005d7SVasant Hegde /* ibm,validate-flash-image update result tokens */ 58f51005d7SVasant Hegde #define VALIDATE_TMP_UPDATE 0 /* T side will be updated */ 59f51005d7SVasant Hegde #define VALIDATE_FLASH_AUTH 1 /* Partition does not have authority */ 60f51005d7SVasant Hegde #define VALIDATE_INVALID_IMG 2 /* Candidate image is not valid */ 61f51005d7SVasant Hegde #define VALIDATE_CUR_UNKNOWN 3 /* Current fixpack level is unknown */ 62f51005d7SVasant Hegde /* 63f51005d7SVasant Hegde * Current T side will be committed to P side before being replace with new 64f51005d7SVasant Hegde * image, and the new image is downlevel from current image 65f51005d7SVasant Hegde */ 66f51005d7SVasant Hegde #define VALIDATE_TMP_COMMIT_DL 4 67f51005d7SVasant Hegde /* 68f51005d7SVasant Hegde * Current T side will be committed to P side before being replaced with new 69f51005d7SVasant Hegde * image 70f51005d7SVasant Hegde */ 71f51005d7SVasant Hegde #define VALIDATE_TMP_COMMIT 5 72f51005d7SVasant Hegde /* 73f51005d7SVasant Hegde * T side will be updated with a downlevel image 74f51005d7SVasant Hegde */ 75f51005d7SVasant Hegde #define VALIDATE_TMP_UPDATE_DL 6 76fd2d37c8SVasant Hegde /* 77fd2d37c8SVasant Hegde * The candidate image's release date is later than the system's firmware 78fd2d37c8SVasant Hegde * service entitlement date - service warranty period has expired 79fd2d37c8SVasant Hegde */ 80fd2d37c8SVasant Hegde #define VALIDATE_OUT_OF_WRNTY 7 81f4fcbbe9SPaul Mackerras 82f4fcbbe9SPaul Mackerras /* ibm,manage-flash-image operation tokens */ 83f4fcbbe9SPaul Mackerras #define RTAS_REJECT_TMP_IMG 0 84f4fcbbe9SPaul Mackerras #define RTAS_COMMIT_TMP_IMG 1 85f4fcbbe9SPaul Mackerras 86f4fcbbe9SPaul Mackerras /* Array sizes */ 87f4fcbbe9SPaul Mackerras #define VALIDATE_BUF_SIZE 4096 88a94a1472SVasant Hegde #define VALIDATE_MSG_LEN 256 89f4fcbbe9SPaul Mackerras #define RTAS_MSG_MAXLEN 64 90f4fcbbe9SPaul Mackerras 91ae883cabSJohn Rose /* Quirk - RTAS requires 4k list length and block size */ 92ae883cabSJohn Rose #define RTAS_BLKLIST_LENGTH 4096 93ae883cabSJohn Rose #define RTAS_BLK_SIZE 4096 94ae883cabSJohn Rose 95f4fcbbe9SPaul Mackerras struct flash_block { 96f4fcbbe9SPaul Mackerras char *data; 97f4fcbbe9SPaul Mackerras unsigned long length; 98f4fcbbe9SPaul Mackerras }; 99f4fcbbe9SPaul Mackerras 100f4fcbbe9SPaul Mackerras /* This struct is very similar but not identical to 101f4fcbbe9SPaul Mackerras * that needed by the rtas flash update. 102f4fcbbe9SPaul Mackerras * All we need to do for rtas is rewrite num_blocks 103f4fcbbe9SPaul Mackerras * into a version/length and translate the pointers 104f4fcbbe9SPaul Mackerras * to absolute. 105f4fcbbe9SPaul Mackerras */ 106ae883cabSJohn Rose #define FLASH_BLOCKS_PER_NODE ((RTAS_BLKLIST_LENGTH - 16) / sizeof(struct flash_block)) 107f4fcbbe9SPaul Mackerras struct flash_block_list { 108f4fcbbe9SPaul Mackerras unsigned long num_blocks; 109f4fcbbe9SPaul Mackerras struct flash_block_list *next; 110f4fcbbe9SPaul Mackerras struct flash_block blocks[FLASH_BLOCKS_PER_NODE]; 111f4fcbbe9SPaul Mackerras }; 112f4fcbbe9SPaul Mackerras 113bd2b64a1SMilton Miller static struct flash_block_list *rtas_firmware_flash_list; 114f4fcbbe9SPaul Mackerras 115ae883cabSJohn Rose /* Use slab cache to guarantee 4k alignment */ 116e18b890bSChristoph Lameter static struct kmem_cache *flash_block_cache = NULL; 117ae883cabSJohn Rose 118f4fcbbe9SPaul Mackerras #define FLASH_BLOCK_LIST_VERSION (1UL) 119f4fcbbe9SPaul Mackerras 120e8eeded3SDavid Howells /* 121e8eeded3SDavid Howells * Local copy of the flash block list. 122e8eeded3SDavid Howells * 123e8eeded3SDavid Howells * The rtas_firmware_flash_list varable will be 124bd2b64a1SMilton Miller * set once the data is fully read. 125f4fcbbe9SPaul Mackerras * 126f4fcbbe9SPaul Mackerras * For convenience as we build the list we use virtual addrs, 127f4fcbbe9SPaul Mackerras * we do not fill in the version number, and the length field 128f4fcbbe9SPaul Mackerras * is treated as the number of entries currently in the block 129bd2b64a1SMilton Miller * (i.e. not a byte count). This is all fixed when calling 130bd2b64a1SMilton Miller * the flash routine. 131f4fcbbe9SPaul Mackerras */ 132f4fcbbe9SPaul Mackerras 133f4fcbbe9SPaul Mackerras /* Status int must be first member of struct */ 134f4fcbbe9SPaul Mackerras struct rtas_update_flash_t 135f4fcbbe9SPaul Mackerras { 136f4fcbbe9SPaul Mackerras int status; /* Flash update status */ 137f4fcbbe9SPaul Mackerras struct flash_block_list *flist; /* Local copy of flash block list */ 138f4fcbbe9SPaul Mackerras }; 139f4fcbbe9SPaul Mackerras 140f4fcbbe9SPaul Mackerras /* Status int must be first member of struct */ 141f4fcbbe9SPaul Mackerras struct rtas_manage_flash_t 142f4fcbbe9SPaul Mackerras { 143f4fcbbe9SPaul Mackerras int status; /* Returned status */ 144f4fcbbe9SPaul Mackerras }; 145f4fcbbe9SPaul Mackerras 146f4fcbbe9SPaul Mackerras /* Status int must be first member of struct */ 147f4fcbbe9SPaul Mackerras struct rtas_validate_flash_t 148f4fcbbe9SPaul Mackerras { 149f4fcbbe9SPaul Mackerras int status; /* Returned status */ 150e8eeded3SDavid Howells char *buf; /* Candidate image buffer */ 151f4fcbbe9SPaul Mackerras unsigned int buf_size; /* Size of image buf */ 152f4fcbbe9SPaul Mackerras unsigned int update_results; /* Update results token */ 153f4fcbbe9SPaul Mackerras }; 154f4fcbbe9SPaul Mackerras 155e8eeded3SDavid Howells static struct rtas_update_flash_t rtas_update_flash_data; 156e8eeded3SDavid Howells static struct rtas_manage_flash_t rtas_manage_flash_data; 157e8eeded3SDavid Howells static struct rtas_validate_flash_t rtas_validate_flash_data; 158e8eeded3SDavid Howells static DEFINE_MUTEX(rtas_update_flash_mutex); 159e8eeded3SDavid Howells static DEFINE_MUTEX(rtas_manage_flash_mutex); 160e8eeded3SDavid Howells static DEFINE_MUTEX(rtas_validate_flash_mutex); 161f4fcbbe9SPaul Mackerras 162f4fcbbe9SPaul Mackerras /* Do simple sanity checks on the flash image. */ 163f4fcbbe9SPaul Mackerras static int flash_list_valid(struct flash_block_list *flist) 164f4fcbbe9SPaul Mackerras { 165f4fcbbe9SPaul Mackerras struct flash_block_list *f; 166f4fcbbe9SPaul Mackerras int i; 167f4fcbbe9SPaul Mackerras unsigned long block_size, image_size; 168f4fcbbe9SPaul Mackerras 169f4fcbbe9SPaul Mackerras /* Paranoid self test here. We also collect the image size. */ 170f4fcbbe9SPaul Mackerras image_size = 0; 171f4fcbbe9SPaul Mackerras for (f = flist; f; f = f->next) { 172f4fcbbe9SPaul Mackerras for (i = 0; i < f->num_blocks; i++) { 173f4fcbbe9SPaul Mackerras if (f->blocks[i].data == NULL) { 174f4fcbbe9SPaul Mackerras return FLASH_IMG_NULL_DATA; 175f4fcbbe9SPaul Mackerras } 176f4fcbbe9SPaul Mackerras block_size = f->blocks[i].length; 177ae883cabSJohn Rose if (block_size <= 0 || block_size > RTAS_BLK_SIZE) { 178f4fcbbe9SPaul Mackerras return FLASH_IMG_BAD_LEN; 179f4fcbbe9SPaul Mackerras } 180f4fcbbe9SPaul Mackerras image_size += block_size; 181f4fcbbe9SPaul Mackerras } 182f4fcbbe9SPaul Mackerras } 183f4fcbbe9SPaul Mackerras 184f4fcbbe9SPaul Mackerras if (image_size < (256 << 10)) { 185f4fcbbe9SPaul Mackerras if (image_size < 2) 186f4fcbbe9SPaul Mackerras return FLASH_NO_OP; 187f4fcbbe9SPaul Mackerras } 188f4fcbbe9SPaul Mackerras 189f4fcbbe9SPaul Mackerras printk(KERN_INFO "FLASH: flash image with %ld bytes stored for hardware flash on reboot\n", image_size); 190f4fcbbe9SPaul Mackerras 191f4fcbbe9SPaul Mackerras return FLASH_IMG_READY; 192f4fcbbe9SPaul Mackerras } 193f4fcbbe9SPaul Mackerras 194f4fcbbe9SPaul Mackerras static void free_flash_list(struct flash_block_list *f) 195f4fcbbe9SPaul Mackerras { 196f4fcbbe9SPaul Mackerras struct flash_block_list *next; 197f4fcbbe9SPaul Mackerras int i; 198f4fcbbe9SPaul Mackerras 199f4fcbbe9SPaul Mackerras while (f) { 200f4fcbbe9SPaul Mackerras for (i = 0; i < f->num_blocks; i++) 201ae883cabSJohn Rose kmem_cache_free(flash_block_cache, f->blocks[i].data); 202f4fcbbe9SPaul Mackerras next = f->next; 203ae883cabSJohn Rose kmem_cache_free(flash_block_cache, f); 204f4fcbbe9SPaul Mackerras f = next; 205f4fcbbe9SPaul Mackerras } 206f4fcbbe9SPaul Mackerras } 207f4fcbbe9SPaul Mackerras 208f4fcbbe9SPaul Mackerras static int rtas_flash_release(struct inode *inode, struct file *file) 209f4fcbbe9SPaul Mackerras { 210e8eeded3SDavid Howells struct rtas_update_flash_t *const uf = &rtas_update_flash_data; 211f4fcbbe9SPaul Mackerras 212e8eeded3SDavid Howells mutex_lock(&rtas_update_flash_mutex); 213e8eeded3SDavid Howells 214f4fcbbe9SPaul Mackerras if (uf->flist) { 215f4fcbbe9SPaul Mackerras /* File was opened in write mode for a new flash attempt */ 216f4fcbbe9SPaul Mackerras /* Clear saved list */ 217bd2b64a1SMilton Miller if (rtas_firmware_flash_list) { 218bd2b64a1SMilton Miller free_flash_list(rtas_firmware_flash_list); 219bd2b64a1SMilton Miller rtas_firmware_flash_list = NULL; 220f4fcbbe9SPaul Mackerras } 221f4fcbbe9SPaul Mackerras 222f4fcbbe9SPaul Mackerras if (uf->status != FLASH_AUTH) 223f4fcbbe9SPaul Mackerras uf->status = flash_list_valid(uf->flist); 224f4fcbbe9SPaul Mackerras 225f4fcbbe9SPaul Mackerras if (uf->status == FLASH_IMG_READY) 226bd2b64a1SMilton Miller rtas_firmware_flash_list = uf->flist; 227f4fcbbe9SPaul Mackerras else 228f4fcbbe9SPaul Mackerras free_flash_list(uf->flist); 229f4fcbbe9SPaul Mackerras 230f4fcbbe9SPaul Mackerras uf->flist = NULL; 231f4fcbbe9SPaul Mackerras } 232f4fcbbe9SPaul Mackerras 233e8eeded3SDavid Howells mutex_unlock(&rtas_update_flash_mutex); 234f4fcbbe9SPaul Mackerras return 0; 235f4fcbbe9SPaul Mackerras } 236f4fcbbe9SPaul Mackerras 237e8eeded3SDavid Howells static size_t get_flash_status_msg(int status, char *buf) 238f4fcbbe9SPaul Mackerras { 239e8eeded3SDavid Howells const char *msg; 240e8eeded3SDavid Howells size_t len; 241f4fcbbe9SPaul Mackerras 242f4fcbbe9SPaul Mackerras switch (status) { 243f4fcbbe9SPaul Mackerras case FLASH_AUTH: 244f4fcbbe9SPaul Mackerras msg = "error: this partition does not have service authority\n"; 245f4fcbbe9SPaul Mackerras break; 246f4fcbbe9SPaul Mackerras case FLASH_NO_OP: 247f4fcbbe9SPaul Mackerras msg = "info: no firmware image for flash\n"; 248f4fcbbe9SPaul Mackerras break; 249f4fcbbe9SPaul Mackerras case FLASH_IMG_SHORT: 250f4fcbbe9SPaul Mackerras msg = "error: flash image short\n"; 251f4fcbbe9SPaul Mackerras break; 252f4fcbbe9SPaul Mackerras case FLASH_IMG_BAD_LEN: 253f4fcbbe9SPaul Mackerras msg = "error: internal error bad length\n"; 254f4fcbbe9SPaul Mackerras break; 255f4fcbbe9SPaul Mackerras case FLASH_IMG_NULL_DATA: 256f4fcbbe9SPaul Mackerras msg = "error: internal error null data\n"; 257f4fcbbe9SPaul Mackerras break; 258f4fcbbe9SPaul Mackerras case FLASH_IMG_READY: 259f4fcbbe9SPaul Mackerras msg = "ready: firmware image ready for flash on reboot\n"; 260f4fcbbe9SPaul Mackerras break; 261f4fcbbe9SPaul Mackerras default: 262e8eeded3SDavid Howells return sprintf(buf, "error: unexpected status value %d\n", 263e8eeded3SDavid Howells status); 264f4fcbbe9SPaul Mackerras } 265f4fcbbe9SPaul Mackerras 266e8eeded3SDavid Howells len = strlen(msg); 267e8eeded3SDavid Howells memcpy(buf, msg, len + 1); 268e8eeded3SDavid Howells return len; 269f4fcbbe9SPaul Mackerras } 270f4fcbbe9SPaul Mackerras 271f4fcbbe9SPaul Mackerras /* Reading the proc file will show status (not the firmware contents) */ 272e8eeded3SDavid Howells static ssize_t rtas_flash_read_msg(struct file *file, char __user *buf, 273f4fcbbe9SPaul Mackerras size_t count, loff_t *ppos) 274f4fcbbe9SPaul Mackerras { 275e8eeded3SDavid Howells struct rtas_update_flash_t *const uf = &rtas_update_flash_data; 276f4fcbbe9SPaul Mackerras char msg[RTAS_MSG_MAXLEN]; 277e8eeded3SDavid Howells size_t len; 278e8eeded3SDavid Howells int status; 279f4fcbbe9SPaul Mackerras 280e8eeded3SDavid Howells mutex_lock(&rtas_update_flash_mutex); 281e8eeded3SDavid Howells status = uf->status; 282e8eeded3SDavid Howells mutex_unlock(&rtas_update_flash_mutex); 283f4fcbbe9SPaul Mackerras 284e8eeded3SDavid Howells /* Read as text message */ 285e8eeded3SDavid Howells len = get_flash_status_msg(status, msg); 286e8eeded3SDavid Howells return simple_read_from_buffer(buf, count, ppos, msg, len); 287f4fcbbe9SPaul Mackerras } 288f4fcbbe9SPaul Mackerras 289e8eeded3SDavid Howells static ssize_t rtas_flash_read_num(struct file *file, char __user *buf, 290e8eeded3SDavid Howells size_t count, loff_t *ppos) 291e8eeded3SDavid Howells { 292e8eeded3SDavid Howells struct rtas_update_flash_t *const uf = &rtas_update_flash_data; 293e8eeded3SDavid Howells char msg[RTAS_MSG_MAXLEN]; 294e8eeded3SDavid Howells int status; 295e8eeded3SDavid Howells 296e8eeded3SDavid Howells mutex_lock(&rtas_update_flash_mutex); 297e8eeded3SDavid Howells status = uf->status; 298e8eeded3SDavid Howells mutex_unlock(&rtas_update_flash_mutex); 299e8eeded3SDavid Howells 300e8eeded3SDavid Howells /* Read as number */ 301e8eeded3SDavid Howells sprintf(msg, "%d\n", status); 3024c4a5cf6SAkinobu Mita return simple_read_from_buffer(buf, count, ppos, msg, strlen(msg)); 303f4fcbbe9SPaul Mackerras } 304f4fcbbe9SPaul Mackerras 305f4fcbbe9SPaul Mackerras /* We could be much more efficient here. But to keep this function 306f4fcbbe9SPaul Mackerras * simple we allocate a page to the block list no matter how small the 307f4fcbbe9SPaul Mackerras * count is. If the system is low on memory it will be just as well 308f4fcbbe9SPaul Mackerras * that we fail.... 309f4fcbbe9SPaul Mackerras */ 310f4fcbbe9SPaul Mackerras static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, 311f4fcbbe9SPaul Mackerras size_t count, loff_t *off) 312f4fcbbe9SPaul Mackerras { 313e8eeded3SDavid Howells struct rtas_update_flash_t *const uf = &rtas_update_flash_data; 314f4fcbbe9SPaul Mackerras char *p; 315e8eeded3SDavid Howells int next_free, rc; 316f4fcbbe9SPaul Mackerras struct flash_block_list *fl; 317f4fcbbe9SPaul Mackerras 318e8eeded3SDavid Howells mutex_lock(&rtas_update_flash_mutex); 319f4fcbbe9SPaul Mackerras 320f4fcbbe9SPaul Mackerras if (uf->status == FLASH_AUTH || count == 0) 321e8eeded3SDavid Howells goto out; /* discard data */ 322f4fcbbe9SPaul Mackerras 323f4fcbbe9SPaul Mackerras /* In the case that the image is not ready for flashing, the memory 324f4fcbbe9SPaul Mackerras * allocated for the block list will be freed upon the release of the 325f4fcbbe9SPaul Mackerras * proc file 326f4fcbbe9SPaul Mackerras */ 327f4fcbbe9SPaul Mackerras if (uf->flist == NULL) { 328fb4696c3SVasant Hegde uf->flist = kmem_cache_zalloc(flash_block_cache, GFP_KERNEL); 329f4fcbbe9SPaul Mackerras if (!uf->flist) 330e8eeded3SDavid Howells goto nomem; 331f4fcbbe9SPaul Mackerras } 332f4fcbbe9SPaul Mackerras 333f4fcbbe9SPaul Mackerras fl = uf->flist; 334f4fcbbe9SPaul Mackerras while (fl->next) 335f4fcbbe9SPaul Mackerras fl = fl->next; /* seek to last block_list for append */ 336f4fcbbe9SPaul Mackerras next_free = fl->num_blocks; 337f4fcbbe9SPaul Mackerras if (next_free == FLASH_BLOCKS_PER_NODE) { 338f4fcbbe9SPaul Mackerras /* Need to allocate another block_list */ 339fb4696c3SVasant Hegde fl->next = kmem_cache_zalloc(flash_block_cache, GFP_KERNEL); 340f4fcbbe9SPaul Mackerras if (!fl->next) 341e8eeded3SDavid Howells goto nomem; 342f4fcbbe9SPaul Mackerras fl = fl->next; 343f4fcbbe9SPaul Mackerras next_free = 0; 344f4fcbbe9SPaul Mackerras } 345f4fcbbe9SPaul Mackerras 346ae883cabSJohn Rose if (count > RTAS_BLK_SIZE) 347ae883cabSJohn Rose count = RTAS_BLK_SIZE; 348fb4696c3SVasant Hegde p = kmem_cache_zalloc(flash_block_cache, GFP_KERNEL); 349f4fcbbe9SPaul Mackerras if (!p) 350e8eeded3SDavid Howells goto nomem; 351f4fcbbe9SPaul Mackerras 352f4fcbbe9SPaul Mackerras if(copy_from_user(p, buffer, count)) { 353ae883cabSJohn Rose kmem_cache_free(flash_block_cache, p); 354e8eeded3SDavid Howells rc = -EFAULT; 355e8eeded3SDavid Howells goto error; 356f4fcbbe9SPaul Mackerras } 357f4fcbbe9SPaul Mackerras fl->blocks[next_free].data = p; 358f4fcbbe9SPaul Mackerras fl->blocks[next_free].length = count; 359f4fcbbe9SPaul Mackerras fl->num_blocks++; 360e8eeded3SDavid Howells out: 361e8eeded3SDavid Howells mutex_unlock(&rtas_update_flash_mutex); 362f4fcbbe9SPaul Mackerras return count; 363e8eeded3SDavid Howells 364e8eeded3SDavid Howells nomem: 365e8eeded3SDavid Howells rc = -ENOMEM; 366e8eeded3SDavid Howells error: 367e8eeded3SDavid Howells mutex_unlock(&rtas_update_flash_mutex); 368e8eeded3SDavid Howells return rc; 369f4fcbbe9SPaul Mackerras } 370f4fcbbe9SPaul Mackerras 371e8eeded3SDavid Howells /* 372e8eeded3SDavid Howells * Flash management routines. 373e8eeded3SDavid Howells */ 374e8eeded3SDavid Howells static void manage_flash(struct rtas_manage_flash_t *args_buf, unsigned int op) 375f4fcbbe9SPaul Mackerras { 376f4fcbbe9SPaul Mackerras s32 rc; 377f4fcbbe9SPaul Mackerras 378507279dbSJohn Rose do { 379e8eeded3SDavid Howells rc = rtas_call(rtas_token("ibm,manage-flash-image"), 1, 1, 380e8eeded3SDavid Howells NULL, op); 381507279dbSJohn Rose } while (rtas_busy_delay(rc)); 382f4fcbbe9SPaul Mackerras 383f4fcbbe9SPaul Mackerras args_buf->status = rc; 384f4fcbbe9SPaul Mackerras } 385f4fcbbe9SPaul Mackerras 386f4fcbbe9SPaul Mackerras static ssize_t manage_flash_read(struct file *file, char __user *buf, 387f4fcbbe9SPaul Mackerras size_t count, loff_t *ppos) 388f4fcbbe9SPaul Mackerras { 389e8eeded3SDavid Howells struct rtas_manage_flash_t *const args_buf = &rtas_manage_flash_data; 390f4fcbbe9SPaul Mackerras char msg[RTAS_MSG_MAXLEN]; 391e8eeded3SDavid Howells int msglen, status; 392f4fcbbe9SPaul Mackerras 393e8eeded3SDavid Howells mutex_lock(&rtas_manage_flash_mutex); 394e8eeded3SDavid Howells status = args_buf->status; 395e8eeded3SDavid Howells mutex_unlock(&rtas_manage_flash_mutex); 396f4fcbbe9SPaul Mackerras 397e8eeded3SDavid Howells msglen = sprintf(msg, "%d\n", status); 3984c4a5cf6SAkinobu Mita return simple_read_from_buffer(buf, count, ppos, msg, msglen); 399f4fcbbe9SPaul Mackerras } 400f4fcbbe9SPaul Mackerras 401f4fcbbe9SPaul Mackerras static ssize_t manage_flash_write(struct file *file, const char __user *buf, 402f4fcbbe9SPaul Mackerras size_t count, loff_t *off) 403f4fcbbe9SPaul Mackerras { 404e8eeded3SDavid Howells struct rtas_manage_flash_t *const args_buf = &rtas_manage_flash_data; 405e8eeded3SDavid Howells static const char reject_str[] = "0"; 406e8eeded3SDavid Howells static const char commit_str[] = "1"; 407f4fcbbe9SPaul Mackerras char stkbuf[10]; 408e8eeded3SDavid Howells int op, rc; 409f4fcbbe9SPaul Mackerras 410e8eeded3SDavid Howells mutex_lock(&rtas_manage_flash_mutex); 411e8eeded3SDavid Howells 412f4fcbbe9SPaul Mackerras if ((args_buf->status == MANAGE_AUTH) || (count == 0)) 413e8eeded3SDavid Howells goto out; 414f4fcbbe9SPaul Mackerras 415f4fcbbe9SPaul Mackerras op = -1; 416f4fcbbe9SPaul Mackerras if (buf) { 417f4fcbbe9SPaul Mackerras if (count > 9) count = 9; 418e8eeded3SDavid Howells rc = -EFAULT; 419e8eeded3SDavid Howells if (copy_from_user (stkbuf, buf, count)) 420e8eeded3SDavid Howells goto error; 421f4fcbbe9SPaul Mackerras if (strncmp(stkbuf, reject_str, strlen(reject_str)) == 0) 422f4fcbbe9SPaul Mackerras op = RTAS_REJECT_TMP_IMG; 423f4fcbbe9SPaul Mackerras else if (strncmp(stkbuf, commit_str, strlen(commit_str)) == 0) 424f4fcbbe9SPaul Mackerras op = RTAS_COMMIT_TMP_IMG; 425f4fcbbe9SPaul Mackerras } 426f4fcbbe9SPaul Mackerras 427e8eeded3SDavid Howells if (op == -1) { /* buf is empty, or contains invalid string */ 428e8eeded3SDavid Howells rc = -EINVAL; 429e8eeded3SDavid Howells goto error; 430f4fcbbe9SPaul Mackerras } 431f4fcbbe9SPaul Mackerras 432e8eeded3SDavid Howells manage_flash(args_buf, op); 433e8eeded3SDavid Howells out: 434e8eeded3SDavid Howells mutex_unlock(&rtas_manage_flash_mutex); 435e8eeded3SDavid Howells return count; 436e8eeded3SDavid Howells 437e8eeded3SDavid Howells error: 438e8eeded3SDavid Howells mutex_unlock(&rtas_manage_flash_mutex); 439e8eeded3SDavid Howells return rc; 440e8eeded3SDavid Howells } 441e8eeded3SDavid Howells 442e8eeded3SDavid Howells /* 443e8eeded3SDavid Howells * Validation routines. 444e8eeded3SDavid Howells */ 445f4fcbbe9SPaul Mackerras static void validate_flash(struct rtas_validate_flash_t *args_buf) 446f4fcbbe9SPaul Mackerras { 447f4fcbbe9SPaul Mackerras int token = rtas_token("ibm,validate-flash-image"); 448f4fcbbe9SPaul Mackerras int update_results; 449f4fcbbe9SPaul Mackerras s32 rc; 450f4fcbbe9SPaul Mackerras 451f4fcbbe9SPaul Mackerras rc = 0; 452507279dbSJohn Rose do { 453f4fcbbe9SPaul Mackerras spin_lock(&rtas_data_buf_lock); 454f4fcbbe9SPaul Mackerras memcpy(rtas_data_buf, args_buf->buf, VALIDATE_BUF_SIZE); 455f4fcbbe9SPaul Mackerras rc = rtas_call(token, 2, 2, &update_results, 456f4fcbbe9SPaul Mackerras (u32) __pa(rtas_data_buf), args_buf->buf_size); 457f4fcbbe9SPaul Mackerras memcpy(args_buf->buf, rtas_data_buf, VALIDATE_BUF_SIZE); 458f4fcbbe9SPaul Mackerras spin_unlock(&rtas_data_buf_lock); 459507279dbSJohn Rose } while (rtas_busy_delay(rc)); 460f4fcbbe9SPaul Mackerras 461f4fcbbe9SPaul Mackerras args_buf->status = rc; 462f4fcbbe9SPaul Mackerras args_buf->update_results = update_results; 463f4fcbbe9SPaul Mackerras } 464f4fcbbe9SPaul Mackerras 465f4fcbbe9SPaul Mackerras static int get_validate_flash_msg(struct rtas_validate_flash_t *args_buf, 466a94a1472SVasant Hegde char *msg, int msglen) 467f4fcbbe9SPaul Mackerras { 468f4fcbbe9SPaul Mackerras int n; 469f4fcbbe9SPaul Mackerras 470f4fcbbe9SPaul Mackerras if (args_buf->status >= VALIDATE_TMP_UPDATE) { 471f4fcbbe9SPaul Mackerras n = sprintf(msg, "%d\n", args_buf->update_results); 472f4fcbbe9SPaul Mackerras if ((args_buf->update_results >= VALIDATE_CUR_UNKNOWN) || 473f4fcbbe9SPaul Mackerras (args_buf->update_results == VALIDATE_TMP_UPDATE)) 474a94a1472SVasant Hegde n += snprintf(msg + n, msglen - n, "%s\n", 475a94a1472SVasant Hegde args_buf->buf); 476f4fcbbe9SPaul Mackerras } else { 477f4fcbbe9SPaul Mackerras n = sprintf(msg, "%d\n", args_buf->status); 478f4fcbbe9SPaul Mackerras } 479f4fcbbe9SPaul Mackerras return n; 480f4fcbbe9SPaul Mackerras } 481f4fcbbe9SPaul Mackerras 482f4fcbbe9SPaul Mackerras static ssize_t validate_flash_read(struct file *file, char __user *buf, 483f4fcbbe9SPaul Mackerras size_t count, loff_t *ppos) 484f4fcbbe9SPaul Mackerras { 485e8eeded3SDavid Howells struct rtas_validate_flash_t *const args_buf = 486e8eeded3SDavid Howells &rtas_validate_flash_data; 487a94a1472SVasant Hegde char msg[VALIDATE_MSG_LEN]; 488f4fcbbe9SPaul Mackerras int msglen; 489f4fcbbe9SPaul Mackerras 490e8eeded3SDavid Howells mutex_lock(&rtas_validate_flash_mutex); 491a94a1472SVasant Hegde msglen = get_validate_flash_msg(args_buf, msg, VALIDATE_MSG_LEN); 492e8eeded3SDavid Howells mutex_unlock(&rtas_validate_flash_mutex); 493f4fcbbe9SPaul Mackerras 4944c4a5cf6SAkinobu Mita return simple_read_from_buffer(buf, count, ppos, msg, msglen); 495f4fcbbe9SPaul Mackerras } 496f4fcbbe9SPaul Mackerras 497f4fcbbe9SPaul Mackerras static ssize_t validate_flash_write(struct file *file, const char __user *buf, 498f4fcbbe9SPaul Mackerras size_t count, loff_t *off) 499f4fcbbe9SPaul Mackerras { 500e8eeded3SDavid Howells struct rtas_validate_flash_t *const args_buf = 501e8eeded3SDavid Howells &rtas_validate_flash_data; 502f4fcbbe9SPaul Mackerras int rc; 503f4fcbbe9SPaul Mackerras 504e8eeded3SDavid Howells mutex_lock(&rtas_validate_flash_mutex); 505f4fcbbe9SPaul Mackerras 506f4fcbbe9SPaul Mackerras /* We are only interested in the first 4K of the 507f4fcbbe9SPaul Mackerras * candidate image */ 508f4fcbbe9SPaul Mackerras if ((*off >= VALIDATE_BUF_SIZE) || 509f4fcbbe9SPaul Mackerras (args_buf->status == VALIDATE_AUTH)) { 510f4fcbbe9SPaul Mackerras *off += count; 511e8eeded3SDavid Howells mutex_unlock(&rtas_validate_flash_mutex); 512f4fcbbe9SPaul Mackerras return count; 513f4fcbbe9SPaul Mackerras } 514f4fcbbe9SPaul Mackerras 515f4fcbbe9SPaul Mackerras if (*off + count >= VALIDATE_BUF_SIZE) { 516f4fcbbe9SPaul Mackerras count = VALIDATE_BUF_SIZE - *off; 517f4fcbbe9SPaul Mackerras args_buf->status = VALIDATE_READY; 518f4fcbbe9SPaul Mackerras } else { 519f4fcbbe9SPaul Mackerras args_buf->status = VALIDATE_INCOMPLETE; 520f4fcbbe9SPaul Mackerras } 521f4fcbbe9SPaul Mackerras 52296d4f267SLinus Torvalds if (!access_ok(buf, count)) { 523f4fcbbe9SPaul Mackerras rc = -EFAULT; 524f4fcbbe9SPaul Mackerras goto done; 525f4fcbbe9SPaul Mackerras } 526f4fcbbe9SPaul Mackerras if (copy_from_user(args_buf->buf + *off, buf, count)) { 527f4fcbbe9SPaul Mackerras rc = -EFAULT; 528f4fcbbe9SPaul Mackerras goto done; 529f4fcbbe9SPaul Mackerras } 530f4fcbbe9SPaul Mackerras 531f4fcbbe9SPaul Mackerras *off += count; 532f4fcbbe9SPaul Mackerras rc = count; 533f4fcbbe9SPaul Mackerras done: 534e8eeded3SDavid Howells mutex_unlock(&rtas_validate_flash_mutex); 535f4fcbbe9SPaul Mackerras return rc; 536f4fcbbe9SPaul Mackerras } 537f4fcbbe9SPaul Mackerras 538f4fcbbe9SPaul Mackerras static int validate_flash_release(struct inode *inode, struct file *file) 539f4fcbbe9SPaul Mackerras { 540e8eeded3SDavid Howells struct rtas_validate_flash_t *const args_buf = 541e8eeded3SDavid Howells &rtas_validate_flash_data; 542f4fcbbe9SPaul Mackerras 543e8eeded3SDavid Howells mutex_lock(&rtas_validate_flash_mutex); 544f4fcbbe9SPaul Mackerras 545f4fcbbe9SPaul Mackerras if (args_buf->status == VALIDATE_READY) { 546f4fcbbe9SPaul Mackerras args_buf->buf_size = VALIDATE_BUF_SIZE; 547f4fcbbe9SPaul Mackerras validate_flash(args_buf); 548f4fcbbe9SPaul Mackerras } 549f4fcbbe9SPaul Mackerras 550e8eeded3SDavid Howells mutex_unlock(&rtas_validate_flash_mutex); 551f4fcbbe9SPaul Mackerras return 0; 552f4fcbbe9SPaul Mackerras } 553f4fcbbe9SPaul Mackerras 554e8eeded3SDavid Howells /* 555e8eeded3SDavid Howells * On-reboot flash update applicator. 556e8eeded3SDavid Howells */ 557f4fcbbe9SPaul Mackerras static void rtas_flash_firmware(int reboot_type) 558f4fcbbe9SPaul Mackerras { 559f4fcbbe9SPaul Mackerras unsigned long image_size; 560f4fcbbe9SPaul Mackerras struct flash_block_list *f, *next, *flist; 561f4fcbbe9SPaul Mackerras unsigned long rtas_block_list; 562f4fcbbe9SPaul Mackerras int i, status, update_token; 563f4fcbbe9SPaul Mackerras 564bd2b64a1SMilton Miller if (rtas_firmware_flash_list == NULL) 565f4fcbbe9SPaul Mackerras return; /* nothing to do */ 566f4fcbbe9SPaul Mackerras 567f4fcbbe9SPaul Mackerras if (reboot_type != SYS_RESTART) { 568f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: firmware flash requires a reboot\n"); 569f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: the firmware image will NOT be flashed\n"); 570f4fcbbe9SPaul Mackerras return; 571f4fcbbe9SPaul Mackerras } 572f4fcbbe9SPaul Mackerras 573f4fcbbe9SPaul Mackerras update_token = rtas_token("ibm,update-flash-64-and-reboot"); 574f4fcbbe9SPaul Mackerras if (update_token == RTAS_UNKNOWN_SERVICE) { 575f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: ibm,update-flash-64-and-reboot " 576f4fcbbe9SPaul Mackerras "is not available -- not a service partition?\n"); 577f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: firmware will not be flashed\n"); 578f4fcbbe9SPaul Mackerras return; 579f4fcbbe9SPaul Mackerras } 580f4fcbbe9SPaul Mackerras 581bd2b64a1SMilton Miller /* 582df17f56dSRavi K. Nittala * Just before starting the firmware flash, cancel the event scan work 583df17f56dSRavi K. Nittala * to avoid any soft lockup issues. 584df17f56dSRavi K. Nittala */ 585df17f56dSRavi K. Nittala rtas_cancel_event_scan(); 586df17f56dSRavi K. Nittala 587df17f56dSRavi K. Nittala /* 588bd2b64a1SMilton Miller * NOTE: the "first" block must be under 4GB, so we create 589bd2b64a1SMilton Miller * an entry with no data blocks in the reserved buffer in 590bd2b64a1SMilton Miller * the kernel data segment. 591f4fcbbe9SPaul Mackerras */ 592bd2b64a1SMilton Miller spin_lock(&rtas_data_buf_lock); 593bd2b64a1SMilton Miller flist = (struct flash_block_list *)&rtas_data_buf[0]; 594bd2b64a1SMilton Miller flist->num_blocks = 0; 595bd2b64a1SMilton Miller flist->next = rtas_firmware_flash_list; 5963d267523SMichael Ellerman rtas_block_list = __pa(flist); 597f4fcbbe9SPaul Mackerras if (rtas_block_list >= 4UL*1024*1024*1024) { 598f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: kernel bug...flash list header addr above 4GB\n"); 599bd2b64a1SMilton Miller spin_unlock(&rtas_data_buf_lock); 600f4fcbbe9SPaul Mackerras return; 601f4fcbbe9SPaul Mackerras } 602f4fcbbe9SPaul Mackerras 603f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: preparing saved firmware image for flash\n"); 604f4fcbbe9SPaul Mackerras /* Update the block_list in place. */ 605bd2b64a1SMilton Miller rtas_firmware_flash_list = NULL; /* too hard to backout on error */ 606f4fcbbe9SPaul Mackerras image_size = 0; 607f4fcbbe9SPaul Mackerras for (f = flist; f; f = next) { 608f4fcbbe9SPaul Mackerras /* Translate data addrs to absolute */ 609f4fcbbe9SPaul Mackerras for (i = 0; i < f->num_blocks; i++) { 610396a3434SThomas Falcon f->blocks[i].data = (char *)cpu_to_be64(__pa(f->blocks[i].data)); 611f4fcbbe9SPaul Mackerras image_size += f->blocks[i].length; 612396a3434SThomas Falcon f->blocks[i].length = cpu_to_be64(f->blocks[i].length); 613f4fcbbe9SPaul Mackerras } 614f4fcbbe9SPaul Mackerras next = f->next; 615f4fcbbe9SPaul Mackerras /* Don't translate NULL pointer for last entry */ 616f4fcbbe9SPaul Mackerras if (f->next) 617396a3434SThomas Falcon f->next = (struct flash_block_list *)cpu_to_be64(__pa(f->next)); 618f4fcbbe9SPaul Mackerras else 619f4fcbbe9SPaul Mackerras f->next = NULL; 620f4fcbbe9SPaul Mackerras /* make num_blocks into the version/length field */ 621f4fcbbe9SPaul Mackerras f->num_blocks = (FLASH_BLOCK_LIST_VERSION << 56) | ((f->num_blocks+1)*16); 622396a3434SThomas Falcon f->num_blocks = cpu_to_be64(f->num_blocks); 623f4fcbbe9SPaul Mackerras } 624f4fcbbe9SPaul Mackerras 625f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: flash image is %ld bytes\n", image_size); 626f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: performing flash and reboot\n"); 627f4fcbbe9SPaul Mackerras rtas_progress("Flashing \n", 0x0); 628f4fcbbe9SPaul Mackerras rtas_progress("Please Wait... ", 0x0); 629f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: this will take several minutes. Do not power off!\n"); 630f4fcbbe9SPaul Mackerras status = rtas_call(update_token, 1, 1, NULL, rtas_block_list); 631f4fcbbe9SPaul Mackerras switch (status) { /* should only get "bad" status */ 632f4fcbbe9SPaul Mackerras case 0: 633f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: success\n"); 634f4fcbbe9SPaul Mackerras break; 635f4fcbbe9SPaul Mackerras case -1: 636f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: hardware error. Firmware may not be not flashed\n"); 637f4fcbbe9SPaul Mackerras break; 638f4fcbbe9SPaul Mackerras case -3: 639f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: image is corrupt or not correct for this platform. Firmware not flashed\n"); 640f4fcbbe9SPaul Mackerras break; 641f4fcbbe9SPaul Mackerras case -4: 642f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: flash failed when partially complete. System may not reboot\n"); 643f4fcbbe9SPaul Mackerras break; 644f4fcbbe9SPaul Mackerras default: 645f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: unknown flash return code %d\n", status); 646f4fcbbe9SPaul Mackerras break; 647f4fcbbe9SPaul Mackerras } 648bd2b64a1SMilton Miller spin_unlock(&rtas_data_buf_lock); 649f4fcbbe9SPaul Mackerras } 650f4fcbbe9SPaul Mackerras 651f4fcbbe9SPaul Mackerras /* 652e8eeded3SDavid Howells * Manifest of proc files to create 653f4fcbbe9SPaul Mackerras */ 654e8eeded3SDavid Howells struct rtas_flash_file { 655e8eeded3SDavid Howells const char *filename; 656e8eeded3SDavid Howells const char *rtas_call_name; 657e8eeded3SDavid Howells int *status; 658*97a32539SAlexey Dobriyan const struct proc_ops ops; 659e8eeded3SDavid Howells }; 660f4fcbbe9SPaul Mackerras 661e8eeded3SDavid Howells static const struct rtas_flash_file rtas_flash_files[] = { 662f4fcbbe9SPaul Mackerras { 663e8eeded3SDavid Howells .filename = "powerpc/rtas/" FIRMWARE_FLASH_NAME, 664e8eeded3SDavid Howells .rtas_call_name = "ibm,update-flash-64-and-reboot", 665e8eeded3SDavid Howells .status = &rtas_update_flash_data.status, 666*97a32539SAlexey Dobriyan .ops.proc_read = rtas_flash_read_msg, 667*97a32539SAlexey Dobriyan .ops.proc_write = rtas_flash_write, 668*97a32539SAlexey Dobriyan .ops.proc_release = rtas_flash_release, 669*97a32539SAlexey Dobriyan .ops.proc_lseek = default_llseek, 670e8eeded3SDavid Howells }, 671e8eeded3SDavid Howells { 672e8eeded3SDavid Howells .filename = "powerpc/rtas/" FIRMWARE_UPDATE_NAME, 673e8eeded3SDavid Howells .rtas_call_name = "ibm,update-flash-64-and-reboot", 674e8eeded3SDavid Howells .status = &rtas_update_flash_data.status, 675*97a32539SAlexey Dobriyan .ops.proc_read = rtas_flash_read_num, 676*97a32539SAlexey Dobriyan .ops.proc_write = rtas_flash_write, 677*97a32539SAlexey Dobriyan .ops.proc_release = rtas_flash_release, 678*97a32539SAlexey Dobriyan .ops.proc_lseek = default_llseek, 679e8eeded3SDavid Howells }, 680e8eeded3SDavid Howells { 681e8eeded3SDavid Howells .filename = "powerpc/rtas/" VALIDATE_FLASH_NAME, 682e8eeded3SDavid Howells .rtas_call_name = "ibm,validate-flash-image", 683e8eeded3SDavid Howells .status = &rtas_validate_flash_data.status, 684*97a32539SAlexey Dobriyan .ops.proc_read = validate_flash_read, 685*97a32539SAlexey Dobriyan .ops.proc_write = validate_flash_write, 686*97a32539SAlexey Dobriyan .ops.proc_release = validate_flash_release, 687*97a32539SAlexey Dobriyan .ops.proc_lseek = default_llseek, 688e8eeded3SDavid Howells }, 689e8eeded3SDavid Howells { 690e8eeded3SDavid Howells .filename = "powerpc/rtas/" MANAGE_FLASH_NAME, 691e8eeded3SDavid Howells .rtas_call_name = "ibm,manage-flash-image", 692e8eeded3SDavid Howells .status = &rtas_manage_flash_data.status, 693*97a32539SAlexey Dobriyan .ops.proc_read = manage_flash_read, 694*97a32539SAlexey Dobriyan .ops.proc_write = manage_flash_write, 695*97a32539SAlexey Dobriyan .ops.proc_lseek = default_llseek, 696f4fcbbe9SPaul Mackerras } 697f4fcbbe9SPaul Mackerras }; 698f4fcbbe9SPaul Mackerras 6991c21a293SMichael Ellerman static int __init rtas_flash_init(void) 700f4fcbbe9SPaul Mackerras { 701e8eeded3SDavid Howells int i; 702f4fcbbe9SPaul Mackerras 703f4fcbbe9SPaul Mackerras if (rtas_token("ibm,update-flash-64-and-reboot") == 704f4fcbbe9SPaul Mackerras RTAS_UNKNOWN_SERVICE) { 7050e384983SAnton Blanchard pr_info("rtas_flash: no firmware flash support\n"); 7060c930692SAnton Blanchard return -EINVAL; 707f4fcbbe9SPaul Mackerras } 708f4fcbbe9SPaul Mackerras 709e8eeded3SDavid Howells rtas_validate_flash_data.buf = kzalloc(VALIDATE_BUF_SIZE, GFP_KERNEL); 710e8eeded3SDavid Howells if (!rtas_validate_flash_data.buf) 711e8eeded3SDavid Howells return -ENOMEM; 712ae883cabSJohn Rose 713ae883cabSJohn Rose flash_block_cache = kmem_cache_create("rtas_flash_cache", 714ae883cabSJohn Rose RTAS_BLK_SIZE, RTAS_BLK_SIZE, 0, 715fb4696c3SVasant Hegde NULL); 716ae883cabSJohn Rose if (!flash_block_cache) { 717ae883cabSJohn Rose printk(KERN_ERR "%s: failed to create block cache\n", 718e48b1b45SHarvey Harrison __func__); 719e8eeded3SDavid Howells goto enomem_buf; 720ae883cabSJohn Rose } 721e8eeded3SDavid Howells 722e8eeded3SDavid Howells for (i = 0; i < ARRAY_SIZE(rtas_flash_files); i++) { 723e8eeded3SDavid Howells const struct rtas_flash_file *f = &rtas_flash_files[i]; 724e8eeded3SDavid Howells int token; 725e8eeded3SDavid Howells 726*97a32539SAlexey Dobriyan if (!proc_create(f->filename, 0600, NULL, &f->ops)) 727e8eeded3SDavid Howells goto enomem; 728e8eeded3SDavid Howells 729e8eeded3SDavid Howells /* 730e8eeded3SDavid Howells * This code assumes that the status int is the first member of the 731e8eeded3SDavid Howells * struct 732e8eeded3SDavid Howells */ 733e8eeded3SDavid Howells token = rtas_token(f->rtas_call_name); 734e8eeded3SDavid Howells if (token == RTAS_UNKNOWN_SERVICE) 735e8eeded3SDavid Howells *f->status = FLASH_AUTH; 736e8eeded3SDavid Howells else 737e8eeded3SDavid Howells *f->status = FLASH_NO_OP; 738e8eeded3SDavid Howells } 739e8eeded3SDavid Howells 740e8eeded3SDavid Howells rtas_flash_term_hook = rtas_flash_firmware; 741f4fcbbe9SPaul Mackerras return 0; 742f4fcbbe9SPaul Mackerras 743e8eeded3SDavid Howells enomem: 744e8eeded3SDavid Howells while (--i >= 0) { 745e8eeded3SDavid Howells const struct rtas_flash_file *f = &rtas_flash_files[i]; 746e8eeded3SDavid Howells remove_proc_entry(f->filename, NULL); 747e8eeded3SDavid Howells } 748f4fcbbe9SPaul Mackerras 749e8eeded3SDavid Howells kmem_cache_destroy(flash_block_cache); 750e8eeded3SDavid Howells enomem_buf: 751e8eeded3SDavid Howells kfree(rtas_validate_flash_data.buf); 752e8eeded3SDavid Howells return -ENOMEM; 753f4fcbbe9SPaul Mackerras } 754f4fcbbe9SPaul Mackerras 7551c21a293SMichael Ellerman static void __exit rtas_flash_cleanup(void) 756f4fcbbe9SPaul Mackerras { 757e8eeded3SDavid Howells int i; 758e8eeded3SDavid Howells 759f4fcbbe9SPaul Mackerras rtas_flash_term_hook = NULL; 760ae883cabSJohn Rose 761ad18a364SVasant Hegde if (rtas_firmware_flash_list) { 762ad18a364SVasant Hegde free_flash_list(rtas_firmware_flash_list); 763ad18a364SVasant Hegde rtas_firmware_flash_list = NULL; 764ad18a364SVasant Hegde } 765ad18a364SVasant Hegde 766e8eeded3SDavid Howells for (i = 0; i < ARRAY_SIZE(rtas_flash_files); i++) { 767e8eeded3SDavid Howells const struct rtas_flash_file *f = &rtas_flash_files[i]; 768e8eeded3SDavid Howells remove_proc_entry(f->filename, NULL); 769e8eeded3SDavid Howells } 770ae883cabSJohn Rose 771e8eeded3SDavid Howells kmem_cache_destroy(flash_block_cache); 772e8eeded3SDavid Howells kfree(rtas_validate_flash_data.buf); 773f4fcbbe9SPaul Mackerras } 774f4fcbbe9SPaul Mackerras 775f4fcbbe9SPaul Mackerras module_init(rtas_flash_init); 776f4fcbbe9SPaul Mackerras module_exit(rtas_flash_cleanup); 777f4fcbbe9SPaul Mackerras MODULE_LICENSE("GPL"); 778