1f4fcbbe9SPaul Mackerras /* 2f4fcbbe9SPaul Mackerras * c 2001 PPC 64 Team, IBM Corp 3f4fcbbe9SPaul Mackerras * 4f4fcbbe9SPaul Mackerras * This program is free software; you can redistribute it and/or 5f4fcbbe9SPaul Mackerras * modify it under the terms of the GNU General Public License 6f4fcbbe9SPaul Mackerras * as published by the Free Software Foundation; either version 7f4fcbbe9SPaul Mackerras * 2 of the License, or (at your option) any later version. 8f4fcbbe9SPaul Mackerras * 9188917e1SBenjamin Herrenschmidt * /proc/powerpc/rtas/firmware_flash interface 10f4fcbbe9SPaul Mackerras * 11f4fcbbe9SPaul Mackerras * This file implements a firmware_flash interface to pump a firmware 12f4fcbbe9SPaul Mackerras * image into the kernel. At reboot time rtas_restart() will see the 13f4fcbbe9SPaul Mackerras * firmware image and flash it as it reboots (see rtas.c). 14f4fcbbe9SPaul Mackerras */ 15f4fcbbe9SPaul Mackerras 16f4fcbbe9SPaul Mackerras #include <linux/module.h> 17f4fcbbe9SPaul Mackerras #include <linux/init.h> 185a0e3ad6STejun Heo #include <linux/slab.h> 19f4fcbbe9SPaul Mackerras #include <linux/proc_fs.h> 20f4fcbbe9SPaul Mackerras #include <asm/delay.h> 21f4fcbbe9SPaul Mackerras #include <asm/uaccess.h> 22f4fcbbe9SPaul Mackerras #include <asm/rtas.h> 23f4fcbbe9SPaul Mackerras #include <asm/abs_addr.h> 24f4fcbbe9SPaul Mackerras 25f4fcbbe9SPaul Mackerras #define MODULE_VERS "1.0" 26f4fcbbe9SPaul Mackerras #define MODULE_NAME "rtas_flash" 27f4fcbbe9SPaul Mackerras 28f4fcbbe9SPaul Mackerras #define FIRMWARE_FLASH_NAME "firmware_flash" 29f4fcbbe9SPaul Mackerras #define FIRMWARE_UPDATE_NAME "firmware_update" 30f4fcbbe9SPaul Mackerras #define MANAGE_FLASH_NAME "manage_flash" 31f4fcbbe9SPaul Mackerras #define VALIDATE_FLASH_NAME "validate_flash" 32f4fcbbe9SPaul Mackerras 33f4fcbbe9SPaul Mackerras /* General RTAS Status Codes */ 34f4fcbbe9SPaul Mackerras #define RTAS_RC_SUCCESS 0 35f4fcbbe9SPaul Mackerras #define RTAS_RC_HW_ERR -1 36f4fcbbe9SPaul Mackerras #define RTAS_RC_BUSY -2 37f4fcbbe9SPaul Mackerras 38f4fcbbe9SPaul Mackerras /* Flash image status values */ 39f4fcbbe9SPaul Mackerras #define FLASH_AUTH -9002 /* RTAS Not Service Authority Partition */ 40f4fcbbe9SPaul Mackerras #define FLASH_NO_OP -1099 /* No operation initiated by user */ 41f4fcbbe9SPaul Mackerras #define FLASH_IMG_SHORT -1005 /* Flash image shorter than expected */ 42f4fcbbe9SPaul Mackerras #define FLASH_IMG_BAD_LEN -1004 /* Bad length value in flash list block */ 43f4fcbbe9SPaul Mackerras #define FLASH_IMG_NULL_DATA -1003 /* Bad data value in flash list block */ 44f4fcbbe9SPaul Mackerras #define FLASH_IMG_READY 0 /* Firmware img ready for flash on reboot */ 45f4fcbbe9SPaul Mackerras 46f4fcbbe9SPaul Mackerras /* Manage image status values */ 47f4fcbbe9SPaul Mackerras #define MANAGE_AUTH -9002 /* RTAS Not Service Authority Partition */ 48f4fcbbe9SPaul Mackerras #define MANAGE_ACTIVE_ERR -9001 /* RTAS Cannot Overwrite Active Img */ 49f4fcbbe9SPaul Mackerras #define MANAGE_NO_OP -1099 /* No operation initiated by user */ 50f4fcbbe9SPaul Mackerras #define MANAGE_PARAM_ERR -3 /* RTAS Parameter Error */ 51f4fcbbe9SPaul Mackerras #define MANAGE_HW_ERR -1 /* RTAS Hardware Error */ 52f4fcbbe9SPaul Mackerras 53f4fcbbe9SPaul Mackerras /* Validate image status values */ 54f4fcbbe9SPaul Mackerras #define VALIDATE_AUTH -9002 /* RTAS Not Service Authority Partition */ 55f4fcbbe9SPaul Mackerras #define VALIDATE_NO_OP -1099 /* No operation initiated by the user */ 56f4fcbbe9SPaul Mackerras #define VALIDATE_INCOMPLETE -1002 /* User copied < VALIDATE_BUF_SIZE */ 57f4fcbbe9SPaul Mackerras #define VALIDATE_READY -1001 /* Firmware image ready for validation */ 58f4fcbbe9SPaul Mackerras #define VALIDATE_PARAM_ERR -3 /* RTAS Parameter Error */ 59f4fcbbe9SPaul Mackerras #define VALIDATE_HW_ERR -1 /* RTAS Hardware Error */ 60f4fcbbe9SPaul Mackerras #define VALIDATE_TMP_UPDATE 0 /* Validate Return Status */ 61f4fcbbe9SPaul Mackerras #define VALIDATE_FLASH_AUTH 1 /* Validate Return Status */ 62f4fcbbe9SPaul Mackerras #define VALIDATE_INVALID_IMG 2 /* Validate Return Status */ 63f4fcbbe9SPaul Mackerras #define VALIDATE_CUR_UNKNOWN 3 /* Validate Return Status */ 64f4fcbbe9SPaul Mackerras #define VALIDATE_TMP_COMMIT_DL 4 /* Validate Return Status */ 65f4fcbbe9SPaul Mackerras #define VALIDATE_TMP_COMMIT 5 /* Validate Return Status */ 66f4fcbbe9SPaul Mackerras #define VALIDATE_TMP_UPDATE_DL 6 /* Validate Return Status */ 67f4fcbbe9SPaul Mackerras 68f4fcbbe9SPaul Mackerras /* ibm,manage-flash-image operation tokens */ 69f4fcbbe9SPaul Mackerras #define RTAS_REJECT_TMP_IMG 0 70f4fcbbe9SPaul Mackerras #define RTAS_COMMIT_TMP_IMG 1 71f4fcbbe9SPaul Mackerras 72f4fcbbe9SPaul Mackerras /* Array sizes */ 73f4fcbbe9SPaul Mackerras #define VALIDATE_BUF_SIZE 4096 74f4fcbbe9SPaul Mackerras #define RTAS_MSG_MAXLEN 64 75f4fcbbe9SPaul Mackerras 76ae883cabSJohn Rose /* Quirk - RTAS requires 4k list length and block size */ 77ae883cabSJohn Rose #define RTAS_BLKLIST_LENGTH 4096 78ae883cabSJohn Rose #define RTAS_BLK_SIZE 4096 79ae883cabSJohn Rose 80f4fcbbe9SPaul Mackerras struct flash_block { 81f4fcbbe9SPaul Mackerras char *data; 82f4fcbbe9SPaul Mackerras unsigned long length; 83f4fcbbe9SPaul Mackerras }; 84f4fcbbe9SPaul Mackerras 85f4fcbbe9SPaul Mackerras /* This struct is very similar but not identical to 86f4fcbbe9SPaul Mackerras * that needed by the rtas flash update. 87f4fcbbe9SPaul Mackerras * All we need to do for rtas is rewrite num_blocks 88f4fcbbe9SPaul Mackerras * into a version/length and translate the pointers 89f4fcbbe9SPaul Mackerras * to absolute. 90f4fcbbe9SPaul Mackerras */ 91ae883cabSJohn Rose #define FLASH_BLOCKS_PER_NODE ((RTAS_BLKLIST_LENGTH - 16) / sizeof(struct flash_block)) 92f4fcbbe9SPaul Mackerras struct flash_block_list { 93f4fcbbe9SPaul Mackerras unsigned long num_blocks; 94f4fcbbe9SPaul Mackerras struct flash_block_list *next; 95f4fcbbe9SPaul Mackerras struct flash_block blocks[FLASH_BLOCKS_PER_NODE]; 96f4fcbbe9SPaul Mackerras }; 97f4fcbbe9SPaul Mackerras 98*bd2b64a1SMilton Miller static struct flash_block_list *rtas_firmware_flash_list; 99f4fcbbe9SPaul Mackerras 100ae883cabSJohn Rose /* Use slab cache to guarantee 4k alignment */ 101e18b890bSChristoph Lameter static struct kmem_cache *flash_block_cache = NULL; 102ae883cabSJohn Rose 103f4fcbbe9SPaul Mackerras #define FLASH_BLOCK_LIST_VERSION (1UL) 104f4fcbbe9SPaul Mackerras 105f4fcbbe9SPaul Mackerras /* Local copy of the flash block list. 106f4fcbbe9SPaul Mackerras * We only allow one open of the flash proc file and create this 107*bd2b64a1SMilton Miller * list as we go. The rtas_firmware_flash_list varable will be 108*bd2b64a1SMilton Miller * set once the data is fully read. 109f4fcbbe9SPaul Mackerras * 110f4fcbbe9SPaul Mackerras * For convenience as we build the list we use virtual addrs, 111f4fcbbe9SPaul Mackerras * we do not fill in the version number, and the length field 112f4fcbbe9SPaul Mackerras * is treated as the number of entries currently in the block 113*bd2b64a1SMilton Miller * (i.e. not a byte count). This is all fixed when calling 114*bd2b64a1SMilton Miller * the flash routine. 115f4fcbbe9SPaul Mackerras */ 116f4fcbbe9SPaul Mackerras 117f4fcbbe9SPaul Mackerras /* Status int must be first member of struct */ 118f4fcbbe9SPaul Mackerras struct rtas_update_flash_t 119f4fcbbe9SPaul Mackerras { 120f4fcbbe9SPaul Mackerras int status; /* Flash update status */ 121f4fcbbe9SPaul Mackerras struct flash_block_list *flist; /* Local copy of flash block list */ 122f4fcbbe9SPaul Mackerras }; 123f4fcbbe9SPaul Mackerras 124f4fcbbe9SPaul Mackerras /* Status int must be first member of struct */ 125f4fcbbe9SPaul Mackerras struct rtas_manage_flash_t 126f4fcbbe9SPaul Mackerras { 127f4fcbbe9SPaul Mackerras int status; /* Returned status */ 128f4fcbbe9SPaul Mackerras unsigned int op; /* Reject or commit image */ 129f4fcbbe9SPaul Mackerras }; 130f4fcbbe9SPaul Mackerras 131f4fcbbe9SPaul Mackerras /* Status int must be first member of struct */ 132f4fcbbe9SPaul Mackerras struct rtas_validate_flash_t 133f4fcbbe9SPaul Mackerras { 134f4fcbbe9SPaul Mackerras int status; /* Returned status */ 135f4fcbbe9SPaul Mackerras char buf[VALIDATE_BUF_SIZE]; /* Candidate image buffer */ 136f4fcbbe9SPaul Mackerras unsigned int buf_size; /* Size of image buf */ 137f4fcbbe9SPaul Mackerras unsigned int update_results; /* Update results token */ 138f4fcbbe9SPaul Mackerras }; 139f4fcbbe9SPaul Mackerras 140f4fcbbe9SPaul Mackerras static DEFINE_SPINLOCK(flash_file_open_lock); 141f4fcbbe9SPaul Mackerras static struct proc_dir_entry *firmware_flash_pde; 142f4fcbbe9SPaul Mackerras static struct proc_dir_entry *firmware_update_pde; 143f4fcbbe9SPaul Mackerras static struct proc_dir_entry *validate_pde; 144f4fcbbe9SPaul Mackerras static struct proc_dir_entry *manage_pde; 145f4fcbbe9SPaul Mackerras 146f4fcbbe9SPaul Mackerras /* Do simple sanity checks on the flash image. */ 147f4fcbbe9SPaul Mackerras static int flash_list_valid(struct flash_block_list *flist) 148f4fcbbe9SPaul Mackerras { 149f4fcbbe9SPaul Mackerras struct flash_block_list *f; 150f4fcbbe9SPaul Mackerras int i; 151f4fcbbe9SPaul Mackerras unsigned long block_size, image_size; 152f4fcbbe9SPaul Mackerras 153f4fcbbe9SPaul Mackerras /* Paranoid self test here. We also collect the image size. */ 154f4fcbbe9SPaul Mackerras image_size = 0; 155f4fcbbe9SPaul Mackerras for (f = flist; f; f = f->next) { 156f4fcbbe9SPaul Mackerras for (i = 0; i < f->num_blocks; i++) { 157f4fcbbe9SPaul Mackerras if (f->blocks[i].data == NULL) { 158f4fcbbe9SPaul Mackerras return FLASH_IMG_NULL_DATA; 159f4fcbbe9SPaul Mackerras } 160f4fcbbe9SPaul Mackerras block_size = f->blocks[i].length; 161ae883cabSJohn Rose if (block_size <= 0 || block_size > RTAS_BLK_SIZE) { 162f4fcbbe9SPaul Mackerras return FLASH_IMG_BAD_LEN; 163f4fcbbe9SPaul Mackerras } 164f4fcbbe9SPaul Mackerras image_size += block_size; 165f4fcbbe9SPaul Mackerras } 166f4fcbbe9SPaul Mackerras } 167f4fcbbe9SPaul Mackerras 168f4fcbbe9SPaul Mackerras if (image_size < (256 << 10)) { 169f4fcbbe9SPaul Mackerras if (image_size < 2) 170f4fcbbe9SPaul Mackerras return FLASH_NO_OP; 171f4fcbbe9SPaul Mackerras } 172f4fcbbe9SPaul Mackerras 173f4fcbbe9SPaul Mackerras printk(KERN_INFO "FLASH: flash image with %ld bytes stored for hardware flash on reboot\n", image_size); 174f4fcbbe9SPaul Mackerras 175f4fcbbe9SPaul Mackerras return FLASH_IMG_READY; 176f4fcbbe9SPaul Mackerras } 177f4fcbbe9SPaul Mackerras 178f4fcbbe9SPaul Mackerras static void free_flash_list(struct flash_block_list *f) 179f4fcbbe9SPaul Mackerras { 180f4fcbbe9SPaul Mackerras struct flash_block_list *next; 181f4fcbbe9SPaul Mackerras int i; 182f4fcbbe9SPaul Mackerras 183f4fcbbe9SPaul Mackerras while (f) { 184f4fcbbe9SPaul Mackerras for (i = 0; i < f->num_blocks; i++) 185ae883cabSJohn Rose kmem_cache_free(flash_block_cache, f->blocks[i].data); 186f4fcbbe9SPaul Mackerras next = f->next; 187ae883cabSJohn Rose kmem_cache_free(flash_block_cache, f); 188f4fcbbe9SPaul Mackerras f = next; 189f4fcbbe9SPaul Mackerras } 190f4fcbbe9SPaul Mackerras } 191f4fcbbe9SPaul Mackerras 192f4fcbbe9SPaul Mackerras static int rtas_flash_release(struct inode *inode, struct file *file) 193f4fcbbe9SPaul Mackerras { 194b4d1ab58SJosef Sipek struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); 195f4fcbbe9SPaul Mackerras struct rtas_update_flash_t *uf; 196f4fcbbe9SPaul Mackerras 197f4fcbbe9SPaul Mackerras uf = (struct rtas_update_flash_t *) dp->data; 198f4fcbbe9SPaul Mackerras if (uf->flist) { 199f4fcbbe9SPaul Mackerras /* File was opened in write mode for a new flash attempt */ 200f4fcbbe9SPaul Mackerras /* Clear saved list */ 201*bd2b64a1SMilton Miller if (rtas_firmware_flash_list) { 202*bd2b64a1SMilton Miller free_flash_list(rtas_firmware_flash_list); 203*bd2b64a1SMilton Miller rtas_firmware_flash_list = NULL; 204f4fcbbe9SPaul Mackerras } 205f4fcbbe9SPaul Mackerras 206f4fcbbe9SPaul Mackerras if (uf->status != FLASH_AUTH) 207f4fcbbe9SPaul Mackerras uf->status = flash_list_valid(uf->flist); 208f4fcbbe9SPaul Mackerras 209f4fcbbe9SPaul Mackerras if (uf->status == FLASH_IMG_READY) 210*bd2b64a1SMilton Miller rtas_firmware_flash_list = uf->flist; 211f4fcbbe9SPaul Mackerras else 212f4fcbbe9SPaul Mackerras free_flash_list(uf->flist); 213f4fcbbe9SPaul Mackerras 214f4fcbbe9SPaul Mackerras uf->flist = NULL; 215f4fcbbe9SPaul Mackerras } 216f4fcbbe9SPaul Mackerras 217f4fcbbe9SPaul Mackerras atomic_dec(&dp->count); 218f4fcbbe9SPaul Mackerras return 0; 219f4fcbbe9SPaul Mackerras } 220f4fcbbe9SPaul Mackerras 221f4fcbbe9SPaul Mackerras static void get_flash_status_msg(int status, char *buf) 222f4fcbbe9SPaul Mackerras { 223f4fcbbe9SPaul Mackerras char *msg; 224f4fcbbe9SPaul Mackerras 225f4fcbbe9SPaul Mackerras switch (status) { 226f4fcbbe9SPaul Mackerras case FLASH_AUTH: 227f4fcbbe9SPaul Mackerras msg = "error: this partition does not have service authority\n"; 228f4fcbbe9SPaul Mackerras break; 229f4fcbbe9SPaul Mackerras case FLASH_NO_OP: 230f4fcbbe9SPaul Mackerras msg = "info: no firmware image for flash\n"; 231f4fcbbe9SPaul Mackerras break; 232f4fcbbe9SPaul Mackerras case FLASH_IMG_SHORT: 233f4fcbbe9SPaul Mackerras msg = "error: flash image short\n"; 234f4fcbbe9SPaul Mackerras break; 235f4fcbbe9SPaul Mackerras case FLASH_IMG_BAD_LEN: 236f4fcbbe9SPaul Mackerras msg = "error: internal error bad length\n"; 237f4fcbbe9SPaul Mackerras break; 238f4fcbbe9SPaul Mackerras case FLASH_IMG_NULL_DATA: 239f4fcbbe9SPaul Mackerras msg = "error: internal error null data\n"; 240f4fcbbe9SPaul Mackerras break; 241f4fcbbe9SPaul Mackerras case FLASH_IMG_READY: 242f4fcbbe9SPaul Mackerras msg = "ready: firmware image ready for flash on reboot\n"; 243f4fcbbe9SPaul Mackerras break; 244f4fcbbe9SPaul Mackerras default: 245f4fcbbe9SPaul Mackerras sprintf(buf, "error: unexpected status value %d\n", status); 246f4fcbbe9SPaul Mackerras return; 247f4fcbbe9SPaul Mackerras } 248f4fcbbe9SPaul Mackerras 249f4fcbbe9SPaul Mackerras strcpy(buf, msg); 250f4fcbbe9SPaul Mackerras } 251f4fcbbe9SPaul Mackerras 252f4fcbbe9SPaul Mackerras /* Reading the proc file will show status (not the firmware contents) */ 253f4fcbbe9SPaul Mackerras static ssize_t rtas_flash_read(struct file *file, char __user *buf, 254f4fcbbe9SPaul Mackerras size_t count, loff_t *ppos) 255f4fcbbe9SPaul Mackerras { 256b4d1ab58SJosef Sipek struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); 257f4fcbbe9SPaul Mackerras struct rtas_update_flash_t *uf; 258f4fcbbe9SPaul Mackerras char msg[RTAS_MSG_MAXLEN]; 259f4fcbbe9SPaul Mackerras int msglen; 260f4fcbbe9SPaul Mackerras 261f4fcbbe9SPaul Mackerras uf = (struct rtas_update_flash_t *) dp->data; 262f4fcbbe9SPaul Mackerras 263f4fcbbe9SPaul Mackerras if (!strcmp(dp->name, FIRMWARE_FLASH_NAME)) { 264f4fcbbe9SPaul Mackerras get_flash_status_msg(uf->status, msg); 265f4fcbbe9SPaul Mackerras } else { /* FIRMWARE_UPDATE_NAME */ 266f4fcbbe9SPaul Mackerras sprintf(msg, "%d\n", uf->status); 267f4fcbbe9SPaul Mackerras } 268f4fcbbe9SPaul Mackerras msglen = strlen(msg); 269f4fcbbe9SPaul Mackerras if (msglen > count) 270f4fcbbe9SPaul Mackerras msglen = count; 271f4fcbbe9SPaul Mackerras 272f4fcbbe9SPaul Mackerras if (ppos && *ppos != 0) 273f4fcbbe9SPaul Mackerras return 0; /* be cheap */ 274f4fcbbe9SPaul Mackerras 275f4fcbbe9SPaul Mackerras if (!access_ok(VERIFY_WRITE, buf, msglen)) 276f4fcbbe9SPaul Mackerras return -EINVAL; 277f4fcbbe9SPaul Mackerras 278f4fcbbe9SPaul Mackerras if (copy_to_user(buf, msg, msglen)) 279f4fcbbe9SPaul Mackerras return -EFAULT; 280f4fcbbe9SPaul Mackerras 281f4fcbbe9SPaul Mackerras if (ppos) 282f4fcbbe9SPaul Mackerras *ppos = msglen; 283f4fcbbe9SPaul Mackerras return msglen; 284f4fcbbe9SPaul Mackerras } 285f4fcbbe9SPaul Mackerras 286ae883cabSJohn Rose /* constructor for flash_block_cache */ 28751cc5068SAlexey Dobriyan void rtas_block_ctor(void *ptr) 288ae883cabSJohn Rose { 289ae883cabSJohn Rose memset(ptr, 0, RTAS_BLK_SIZE); 290ae883cabSJohn Rose } 291ae883cabSJohn Rose 292f4fcbbe9SPaul Mackerras /* We could be much more efficient here. But to keep this function 293f4fcbbe9SPaul Mackerras * simple we allocate a page to the block list no matter how small the 294f4fcbbe9SPaul Mackerras * count is. If the system is low on memory it will be just as well 295f4fcbbe9SPaul Mackerras * that we fail.... 296f4fcbbe9SPaul Mackerras */ 297f4fcbbe9SPaul Mackerras static ssize_t rtas_flash_write(struct file *file, const char __user *buffer, 298f4fcbbe9SPaul Mackerras size_t count, loff_t *off) 299f4fcbbe9SPaul Mackerras { 300b4d1ab58SJosef Sipek struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); 301f4fcbbe9SPaul Mackerras struct rtas_update_flash_t *uf; 302f4fcbbe9SPaul Mackerras char *p; 303f4fcbbe9SPaul Mackerras int next_free; 304f4fcbbe9SPaul Mackerras struct flash_block_list *fl; 305f4fcbbe9SPaul Mackerras 306f4fcbbe9SPaul Mackerras uf = (struct rtas_update_flash_t *) dp->data; 307f4fcbbe9SPaul Mackerras 308f4fcbbe9SPaul Mackerras if (uf->status == FLASH_AUTH || count == 0) 309f4fcbbe9SPaul Mackerras return count; /* discard data */ 310f4fcbbe9SPaul Mackerras 311f4fcbbe9SPaul Mackerras /* In the case that the image is not ready for flashing, the memory 312f4fcbbe9SPaul Mackerras * allocated for the block list will be freed upon the release of the 313f4fcbbe9SPaul Mackerras * proc file 314f4fcbbe9SPaul Mackerras */ 315f4fcbbe9SPaul Mackerras if (uf->flist == NULL) { 316ae883cabSJohn Rose uf->flist = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); 317f4fcbbe9SPaul Mackerras if (!uf->flist) 318f4fcbbe9SPaul Mackerras return -ENOMEM; 319f4fcbbe9SPaul Mackerras } 320f4fcbbe9SPaul Mackerras 321f4fcbbe9SPaul Mackerras fl = uf->flist; 322f4fcbbe9SPaul Mackerras while (fl->next) 323f4fcbbe9SPaul Mackerras fl = fl->next; /* seek to last block_list for append */ 324f4fcbbe9SPaul Mackerras next_free = fl->num_blocks; 325f4fcbbe9SPaul Mackerras if (next_free == FLASH_BLOCKS_PER_NODE) { 326f4fcbbe9SPaul Mackerras /* Need to allocate another block_list */ 327ae883cabSJohn Rose fl->next = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); 328f4fcbbe9SPaul Mackerras if (!fl->next) 329f4fcbbe9SPaul Mackerras return -ENOMEM; 330f4fcbbe9SPaul Mackerras fl = fl->next; 331f4fcbbe9SPaul Mackerras next_free = 0; 332f4fcbbe9SPaul Mackerras } 333f4fcbbe9SPaul Mackerras 334ae883cabSJohn Rose if (count > RTAS_BLK_SIZE) 335ae883cabSJohn Rose count = RTAS_BLK_SIZE; 336ae883cabSJohn Rose p = kmem_cache_alloc(flash_block_cache, GFP_KERNEL); 337f4fcbbe9SPaul Mackerras if (!p) 338f4fcbbe9SPaul Mackerras return -ENOMEM; 339f4fcbbe9SPaul Mackerras 340f4fcbbe9SPaul Mackerras if(copy_from_user(p, buffer, count)) { 341ae883cabSJohn Rose kmem_cache_free(flash_block_cache, p); 342f4fcbbe9SPaul Mackerras return -EFAULT; 343f4fcbbe9SPaul Mackerras } 344f4fcbbe9SPaul Mackerras fl->blocks[next_free].data = p; 345f4fcbbe9SPaul Mackerras fl->blocks[next_free].length = count; 346f4fcbbe9SPaul Mackerras fl->num_blocks++; 347f4fcbbe9SPaul Mackerras 348f4fcbbe9SPaul Mackerras return count; 349f4fcbbe9SPaul Mackerras } 350f4fcbbe9SPaul Mackerras 351f4fcbbe9SPaul Mackerras static int rtas_excl_open(struct inode *inode, struct file *file) 352f4fcbbe9SPaul Mackerras { 353f4fcbbe9SPaul Mackerras struct proc_dir_entry *dp = PDE(inode); 354f4fcbbe9SPaul Mackerras 355f4fcbbe9SPaul Mackerras /* Enforce exclusive open with use count of PDE */ 356f4fcbbe9SPaul Mackerras spin_lock(&flash_file_open_lock); 35774848398SMaxim Shchetynin if (atomic_read(&dp->count) > 2) { 358f4fcbbe9SPaul Mackerras spin_unlock(&flash_file_open_lock); 359f4fcbbe9SPaul Mackerras return -EBUSY; 360f4fcbbe9SPaul Mackerras } 361f4fcbbe9SPaul Mackerras 362f4fcbbe9SPaul Mackerras atomic_inc(&dp->count); 363f4fcbbe9SPaul Mackerras spin_unlock(&flash_file_open_lock); 364f4fcbbe9SPaul Mackerras 365f4fcbbe9SPaul Mackerras return 0; 366f4fcbbe9SPaul Mackerras } 367f4fcbbe9SPaul Mackerras 368f4fcbbe9SPaul Mackerras static int rtas_excl_release(struct inode *inode, struct file *file) 369f4fcbbe9SPaul Mackerras { 370f4fcbbe9SPaul Mackerras struct proc_dir_entry *dp = PDE(inode); 371f4fcbbe9SPaul Mackerras 372f4fcbbe9SPaul Mackerras atomic_dec(&dp->count); 373f4fcbbe9SPaul Mackerras 374f4fcbbe9SPaul Mackerras return 0; 375f4fcbbe9SPaul Mackerras } 376f4fcbbe9SPaul Mackerras 377f4fcbbe9SPaul Mackerras static void manage_flash(struct rtas_manage_flash_t *args_buf) 378f4fcbbe9SPaul Mackerras { 379f4fcbbe9SPaul Mackerras s32 rc; 380f4fcbbe9SPaul Mackerras 381507279dbSJohn Rose do { 382f4fcbbe9SPaul Mackerras rc = rtas_call(rtas_token("ibm,manage-flash-image"), 1, 383f4fcbbe9SPaul Mackerras 1, NULL, args_buf->op); 384507279dbSJohn Rose } while (rtas_busy_delay(rc)); 385f4fcbbe9SPaul Mackerras 386f4fcbbe9SPaul Mackerras args_buf->status = rc; 387f4fcbbe9SPaul Mackerras } 388f4fcbbe9SPaul Mackerras 389f4fcbbe9SPaul Mackerras static ssize_t manage_flash_read(struct file *file, char __user *buf, 390f4fcbbe9SPaul Mackerras size_t count, loff_t *ppos) 391f4fcbbe9SPaul Mackerras { 392b4d1ab58SJosef Sipek struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); 393f4fcbbe9SPaul Mackerras struct rtas_manage_flash_t *args_buf; 394f4fcbbe9SPaul Mackerras char msg[RTAS_MSG_MAXLEN]; 395f4fcbbe9SPaul Mackerras int msglen; 396f4fcbbe9SPaul Mackerras 397f4fcbbe9SPaul Mackerras args_buf = (struct rtas_manage_flash_t *) dp->data; 398f4fcbbe9SPaul Mackerras if (args_buf == NULL) 399f4fcbbe9SPaul Mackerras return 0; 400f4fcbbe9SPaul Mackerras 401f4fcbbe9SPaul Mackerras msglen = sprintf(msg, "%d\n", args_buf->status); 402f4fcbbe9SPaul Mackerras if (msglen > count) 403f4fcbbe9SPaul Mackerras msglen = count; 404f4fcbbe9SPaul Mackerras 405f4fcbbe9SPaul Mackerras if (ppos && *ppos != 0) 406f4fcbbe9SPaul Mackerras return 0; /* be cheap */ 407f4fcbbe9SPaul Mackerras 408f4fcbbe9SPaul Mackerras if (!access_ok(VERIFY_WRITE, buf, msglen)) 409f4fcbbe9SPaul Mackerras return -EINVAL; 410f4fcbbe9SPaul Mackerras 411f4fcbbe9SPaul Mackerras if (copy_to_user(buf, msg, msglen)) 412f4fcbbe9SPaul Mackerras return -EFAULT; 413f4fcbbe9SPaul Mackerras 414f4fcbbe9SPaul Mackerras if (ppos) 415f4fcbbe9SPaul Mackerras *ppos = msglen; 416f4fcbbe9SPaul Mackerras return msglen; 417f4fcbbe9SPaul Mackerras } 418f4fcbbe9SPaul Mackerras 419f4fcbbe9SPaul Mackerras static ssize_t manage_flash_write(struct file *file, const char __user *buf, 420f4fcbbe9SPaul Mackerras size_t count, loff_t *off) 421f4fcbbe9SPaul Mackerras { 422b4d1ab58SJosef Sipek struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); 423f4fcbbe9SPaul Mackerras struct rtas_manage_flash_t *args_buf; 424f4fcbbe9SPaul Mackerras const char reject_str[] = "0"; 425f4fcbbe9SPaul Mackerras const char commit_str[] = "1"; 426f4fcbbe9SPaul Mackerras char stkbuf[10]; 427f4fcbbe9SPaul Mackerras int op; 428f4fcbbe9SPaul Mackerras 429f4fcbbe9SPaul Mackerras args_buf = (struct rtas_manage_flash_t *) dp->data; 430f4fcbbe9SPaul Mackerras if ((args_buf->status == MANAGE_AUTH) || (count == 0)) 431f4fcbbe9SPaul Mackerras return count; 432f4fcbbe9SPaul Mackerras 433f4fcbbe9SPaul Mackerras op = -1; 434f4fcbbe9SPaul Mackerras if (buf) { 435f4fcbbe9SPaul Mackerras if (count > 9) count = 9; 436f4fcbbe9SPaul Mackerras if (copy_from_user (stkbuf, buf, count)) { 437f4fcbbe9SPaul Mackerras return -EFAULT; 438f4fcbbe9SPaul Mackerras } 439f4fcbbe9SPaul Mackerras if (strncmp(stkbuf, reject_str, strlen(reject_str)) == 0) 440f4fcbbe9SPaul Mackerras op = RTAS_REJECT_TMP_IMG; 441f4fcbbe9SPaul Mackerras else if (strncmp(stkbuf, commit_str, strlen(commit_str)) == 0) 442f4fcbbe9SPaul Mackerras op = RTAS_COMMIT_TMP_IMG; 443f4fcbbe9SPaul Mackerras } 444f4fcbbe9SPaul Mackerras 445f4fcbbe9SPaul Mackerras if (op == -1) /* buf is empty, or contains invalid string */ 446f4fcbbe9SPaul Mackerras return -EINVAL; 447f4fcbbe9SPaul Mackerras 448f4fcbbe9SPaul Mackerras args_buf->op = op; 449f4fcbbe9SPaul Mackerras manage_flash(args_buf); 450f4fcbbe9SPaul Mackerras 451f4fcbbe9SPaul Mackerras return count; 452f4fcbbe9SPaul Mackerras } 453f4fcbbe9SPaul Mackerras 454f4fcbbe9SPaul Mackerras static void validate_flash(struct rtas_validate_flash_t *args_buf) 455f4fcbbe9SPaul Mackerras { 456f4fcbbe9SPaul Mackerras int token = rtas_token("ibm,validate-flash-image"); 457f4fcbbe9SPaul Mackerras int update_results; 458f4fcbbe9SPaul Mackerras s32 rc; 459f4fcbbe9SPaul Mackerras 460f4fcbbe9SPaul Mackerras rc = 0; 461507279dbSJohn Rose do { 462f4fcbbe9SPaul Mackerras spin_lock(&rtas_data_buf_lock); 463f4fcbbe9SPaul Mackerras memcpy(rtas_data_buf, args_buf->buf, VALIDATE_BUF_SIZE); 464f4fcbbe9SPaul Mackerras rc = rtas_call(token, 2, 2, &update_results, 465f4fcbbe9SPaul Mackerras (u32) __pa(rtas_data_buf), args_buf->buf_size); 466f4fcbbe9SPaul Mackerras memcpy(args_buf->buf, rtas_data_buf, VALIDATE_BUF_SIZE); 467f4fcbbe9SPaul Mackerras spin_unlock(&rtas_data_buf_lock); 468507279dbSJohn Rose } while (rtas_busy_delay(rc)); 469f4fcbbe9SPaul Mackerras 470f4fcbbe9SPaul Mackerras args_buf->status = rc; 471f4fcbbe9SPaul Mackerras args_buf->update_results = update_results; 472f4fcbbe9SPaul Mackerras } 473f4fcbbe9SPaul Mackerras 474f4fcbbe9SPaul Mackerras static int get_validate_flash_msg(struct rtas_validate_flash_t *args_buf, 475f4fcbbe9SPaul Mackerras char *msg) 476f4fcbbe9SPaul Mackerras { 477f4fcbbe9SPaul Mackerras int n; 478f4fcbbe9SPaul Mackerras 479f4fcbbe9SPaul Mackerras if (args_buf->status >= VALIDATE_TMP_UPDATE) { 480f4fcbbe9SPaul Mackerras n = sprintf(msg, "%d\n", args_buf->update_results); 481f4fcbbe9SPaul Mackerras if ((args_buf->update_results >= VALIDATE_CUR_UNKNOWN) || 482f4fcbbe9SPaul Mackerras (args_buf->update_results == VALIDATE_TMP_UPDATE)) 483f4fcbbe9SPaul Mackerras n += sprintf(msg + n, "%s\n", args_buf->buf); 484f4fcbbe9SPaul Mackerras } else { 485f4fcbbe9SPaul Mackerras n = sprintf(msg, "%d\n", args_buf->status); 486f4fcbbe9SPaul Mackerras } 487f4fcbbe9SPaul Mackerras return n; 488f4fcbbe9SPaul Mackerras } 489f4fcbbe9SPaul Mackerras 490f4fcbbe9SPaul Mackerras static ssize_t validate_flash_read(struct file *file, char __user *buf, 491f4fcbbe9SPaul Mackerras size_t count, loff_t *ppos) 492f4fcbbe9SPaul Mackerras { 493b4d1ab58SJosef Sipek struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); 494f4fcbbe9SPaul Mackerras struct rtas_validate_flash_t *args_buf; 495f4fcbbe9SPaul Mackerras char msg[RTAS_MSG_MAXLEN]; 496f4fcbbe9SPaul Mackerras int msglen; 497f4fcbbe9SPaul Mackerras 498f4fcbbe9SPaul Mackerras args_buf = (struct rtas_validate_flash_t *) dp->data; 499f4fcbbe9SPaul Mackerras 500f4fcbbe9SPaul Mackerras if (ppos && *ppos != 0) 501f4fcbbe9SPaul Mackerras return 0; /* be cheap */ 502f4fcbbe9SPaul Mackerras 503f4fcbbe9SPaul Mackerras msglen = get_validate_flash_msg(args_buf, msg); 504f4fcbbe9SPaul Mackerras if (msglen > count) 505f4fcbbe9SPaul Mackerras msglen = count; 506f4fcbbe9SPaul Mackerras 507f4fcbbe9SPaul Mackerras if (!access_ok(VERIFY_WRITE, buf, msglen)) 508f4fcbbe9SPaul Mackerras return -EINVAL; 509f4fcbbe9SPaul Mackerras 510f4fcbbe9SPaul Mackerras if (copy_to_user(buf, msg, msglen)) 511f4fcbbe9SPaul Mackerras return -EFAULT; 512f4fcbbe9SPaul Mackerras 513f4fcbbe9SPaul Mackerras if (ppos) 514f4fcbbe9SPaul Mackerras *ppos = msglen; 515f4fcbbe9SPaul Mackerras return msglen; 516f4fcbbe9SPaul Mackerras } 517f4fcbbe9SPaul Mackerras 518f4fcbbe9SPaul Mackerras static ssize_t validate_flash_write(struct file *file, const char __user *buf, 519f4fcbbe9SPaul Mackerras size_t count, loff_t *off) 520f4fcbbe9SPaul Mackerras { 521b4d1ab58SJosef Sipek struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); 522f4fcbbe9SPaul Mackerras struct rtas_validate_flash_t *args_buf; 523f4fcbbe9SPaul Mackerras int rc; 524f4fcbbe9SPaul Mackerras 525f4fcbbe9SPaul Mackerras args_buf = (struct rtas_validate_flash_t *) dp->data; 526f4fcbbe9SPaul Mackerras 527f4fcbbe9SPaul Mackerras if (dp->data == NULL) { 528f4fcbbe9SPaul Mackerras dp->data = kmalloc(sizeof(struct rtas_validate_flash_t), 529f4fcbbe9SPaul Mackerras GFP_KERNEL); 530f4fcbbe9SPaul Mackerras if (dp->data == NULL) 531f4fcbbe9SPaul Mackerras return -ENOMEM; 532f4fcbbe9SPaul Mackerras } 533f4fcbbe9SPaul Mackerras 534f4fcbbe9SPaul Mackerras /* We are only interested in the first 4K of the 535f4fcbbe9SPaul Mackerras * candidate image */ 536f4fcbbe9SPaul Mackerras if ((*off >= VALIDATE_BUF_SIZE) || 537f4fcbbe9SPaul Mackerras (args_buf->status == VALIDATE_AUTH)) { 538f4fcbbe9SPaul Mackerras *off += count; 539f4fcbbe9SPaul Mackerras return count; 540f4fcbbe9SPaul Mackerras } 541f4fcbbe9SPaul Mackerras 542f4fcbbe9SPaul Mackerras if (*off + count >= VALIDATE_BUF_SIZE) { 543f4fcbbe9SPaul Mackerras count = VALIDATE_BUF_SIZE - *off; 544f4fcbbe9SPaul Mackerras args_buf->status = VALIDATE_READY; 545f4fcbbe9SPaul Mackerras } else { 546f4fcbbe9SPaul Mackerras args_buf->status = VALIDATE_INCOMPLETE; 547f4fcbbe9SPaul Mackerras } 548f4fcbbe9SPaul Mackerras 549f4fcbbe9SPaul Mackerras if (!access_ok(VERIFY_READ, buf, count)) { 550f4fcbbe9SPaul Mackerras rc = -EFAULT; 551f4fcbbe9SPaul Mackerras goto done; 552f4fcbbe9SPaul Mackerras } 553f4fcbbe9SPaul Mackerras if (copy_from_user(args_buf->buf + *off, buf, count)) { 554f4fcbbe9SPaul Mackerras rc = -EFAULT; 555f4fcbbe9SPaul Mackerras goto done; 556f4fcbbe9SPaul Mackerras } 557f4fcbbe9SPaul Mackerras 558f4fcbbe9SPaul Mackerras *off += count; 559f4fcbbe9SPaul Mackerras rc = count; 560f4fcbbe9SPaul Mackerras done: 561f4fcbbe9SPaul Mackerras if (rc < 0) { 562f4fcbbe9SPaul Mackerras kfree(dp->data); 563f4fcbbe9SPaul Mackerras dp->data = NULL; 564f4fcbbe9SPaul Mackerras } 565f4fcbbe9SPaul Mackerras return rc; 566f4fcbbe9SPaul Mackerras } 567f4fcbbe9SPaul Mackerras 568f4fcbbe9SPaul Mackerras static int validate_flash_release(struct inode *inode, struct file *file) 569f4fcbbe9SPaul Mackerras { 570b4d1ab58SJosef Sipek struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); 571f4fcbbe9SPaul Mackerras struct rtas_validate_flash_t *args_buf; 572f4fcbbe9SPaul Mackerras 573f4fcbbe9SPaul Mackerras args_buf = (struct rtas_validate_flash_t *) dp->data; 574f4fcbbe9SPaul Mackerras 575f4fcbbe9SPaul Mackerras if (args_buf->status == VALIDATE_READY) { 576f4fcbbe9SPaul Mackerras args_buf->buf_size = VALIDATE_BUF_SIZE; 577f4fcbbe9SPaul Mackerras validate_flash(args_buf); 578f4fcbbe9SPaul Mackerras } 579f4fcbbe9SPaul Mackerras 580f4fcbbe9SPaul Mackerras /* The matching atomic_inc was in rtas_excl_open() */ 581f4fcbbe9SPaul Mackerras atomic_dec(&dp->count); 582f4fcbbe9SPaul Mackerras 583f4fcbbe9SPaul Mackerras return 0; 584f4fcbbe9SPaul Mackerras } 585f4fcbbe9SPaul Mackerras 586f4fcbbe9SPaul Mackerras static void rtas_flash_firmware(int reboot_type) 587f4fcbbe9SPaul Mackerras { 588f4fcbbe9SPaul Mackerras unsigned long image_size; 589f4fcbbe9SPaul Mackerras struct flash_block_list *f, *next, *flist; 590f4fcbbe9SPaul Mackerras unsigned long rtas_block_list; 591f4fcbbe9SPaul Mackerras int i, status, update_token; 592f4fcbbe9SPaul Mackerras 593*bd2b64a1SMilton Miller if (rtas_firmware_flash_list == NULL) 594f4fcbbe9SPaul Mackerras return; /* nothing to do */ 595f4fcbbe9SPaul Mackerras 596f4fcbbe9SPaul Mackerras if (reboot_type != SYS_RESTART) { 597f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: firmware flash requires a reboot\n"); 598f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: the firmware image will NOT be flashed\n"); 599f4fcbbe9SPaul Mackerras return; 600f4fcbbe9SPaul Mackerras } 601f4fcbbe9SPaul Mackerras 602f4fcbbe9SPaul Mackerras update_token = rtas_token("ibm,update-flash-64-and-reboot"); 603f4fcbbe9SPaul Mackerras if (update_token == RTAS_UNKNOWN_SERVICE) { 604f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: ibm,update-flash-64-and-reboot " 605f4fcbbe9SPaul Mackerras "is not available -- not a service partition?\n"); 606f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: firmware will not be flashed\n"); 607f4fcbbe9SPaul Mackerras return; 608f4fcbbe9SPaul Mackerras } 609f4fcbbe9SPaul Mackerras 610*bd2b64a1SMilton Miller /* 611*bd2b64a1SMilton Miller * NOTE: the "first" block must be under 4GB, so we create 612*bd2b64a1SMilton Miller * an entry with no data blocks in the reserved buffer in 613*bd2b64a1SMilton Miller * the kernel data segment. 614f4fcbbe9SPaul Mackerras */ 615*bd2b64a1SMilton Miller spin_lock(&rtas_data_buf_lock); 616*bd2b64a1SMilton Miller flist = (struct flash_block_list *)&rtas_data_buf[0]; 617*bd2b64a1SMilton Miller flist->num_blocks = 0; 618*bd2b64a1SMilton Miller flist->next = rtas_firmware_flash_list; 619f4fcbbe9SPaul Mackerras rtas_block_list = virt_to_abs(flist); 620f4fcbbe9SPaul Mackerras if (rtas_block_list >= 4UL*1024*1024*1024) { 621f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: kernel bug...flash list header addr above 4GB\n"); 622*bd2b64a1SMilton Miller spin_unlock(&rtas_data_buf_lock); 623f4fcbbe9SPaul Mackerras return; 624f4fcbbe9SPaul Mackerras } 625f4fcbbe9SPaul Mackerras 626f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: preparing saved firmware image for flash\n"); 627f4fcbbe9SPaul Mackerras /* Update the block_list in place. */ 628*bd2b64a1SMilton Miller rtas_firmware_flash_list = NULL; /* too hard to backout on error */ 629f4fcbbe9SPaul Mackerras image_size = 0; 630f4fcbbe9SPaul Mackerras for (f = flist; f; f = next) { 631f4fcbbe9SPaul Mackerras /* Translate data addrs to absolute */ 632f4fcbbe9SPaul Mackerras for (i = 0; i < f->num_blocks; i++) { 633f4fcbbe9SPaul Mackerras f->blocks[i].data = (char *)virt_to_abs(f->blocks[i].data); 634f4fcbbe9SPaul Mackerras image_size += f->blocks[i].length; 635f4fcbbe9SPaul Mackerras } 636f4fcbbe9SPaul Mackerras next = f->next; 637f4fcbbe9SPaul Mackerras /* Don't translate NULL pointer for last entry */ 638f4fcbbe9SPaul Mackerras if (f->next) 639f4fcbbe9SPaul Mackerras f->next = (struct flash_block_list *)virt_to_abs(f->next); 640f4fcbbe9SPaul Mackerras else 641f4fcbbe9SPaul Mackerras f->next = NULL; 642f4fcbbe9SPaul Mackerras /* make num_blocks into the version/length field */ 643f4fcbbe9SPaul Mackerras f->num_blocks = (FLASH_BLOCK_LIST_VERSION << 56) | ((f->num_blocks+1)*16); 644f4fcbbe9SPaul Mackerras } 645f4fcbbe9SPaul Mackerras 646f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: flash image is %ld bytes\n", image_size); 647f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: performing flash and reboot\n"); 648f4fcbbe9SPaul Mackerras rtas_progress("Flashing \n", 0x0); 649f4fcbbe9SPaul Mackerras rtas_progress("Please Wait... ", 0x0); 650f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: this will take several minutes. Do not power off!\n"); 651f4fcbbe9SPaul Mackerras status = rtas_call(update_token, 1, 1, NULL, rtas_block_list); 652f4fcbbe9SPaul Mackerras switch (status) { /* should only get "bad" status */ 653f4fcbbe9SPaul Mackerras case 0: 654f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: success\n"); 655f4fcbbe9SPaul Mackerras break; 656f4fcbbe9SPaul Mackerras case -1: 657f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: hardware error. Firmware may not be not flashed\n"); 658f4fcbbe9SPaul Mackerras break; 659f4fcbbe9SPaul Mackerras case -3: 660f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: image is corrupt or not correct for this platform. Firmware not flashed\n"); 661f4fcbbe9SPaul Mackerras break; 662f4fcbbe9SPaul Mackerras case -4: 663f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: flash failed when partially complete. System may not reboot\n"); 664f4fcbbe9SPaul Mackerras break; 665f4fcbbe9SPaul Mackerras default: 666f4fcbbe9SPaul Mackerras printk(KERN_ALERT "FLASH: unknown flash return code %d\n", status); 667f4fcbbe9SPaul Mackerras break; 668f4fcbbe9SPaul Mackerras } 669*bd2b64a1SMilton Miller spin_unlock(&rtas_data_buf_lock); 670f4fcbbe9SPaul Mackerras } 671f4fcbbe9SPaul Mackerras 672f4fcbbe9SPaul Mackerras static void remove_flash_pde(struct proc_dir_entry *dp) 673f4fcbbe9SPaul Mackerras { 674f4fcbbe9SPaul Mackerras if (dp) { 675f4fcbbe9SPaul Mackerras kfree(dp->data); 676f4fcbbe9SPaul Mackerras remove_proc_entry(dp->name, dp->parent); 677f4fcbbe9SPaul Mackerras } 678f4fcbbe9SPaul Mackerras } 679f4fcbbe9SPaul Mackerras 680f4fcbbe9SPaul Mackerras static int initialize_flash_pde_data(const char *rtas_call_name, 681f4fcbbe9SPaul Mackerras size_t buf_size, 682f4fcbbe9SPaul Mackerras struct proc_dir_entry *dp) 683f4fcbbe9SPaul Mackerras { 684f4fcbbe9SPaul Mackerras int *status; 685f4fcbbe9SPaul Mackerras int token; 686f4fcbbe9SPaul Mackerras 687f8485350SYan Burman dp->data = kzalloc(buf_size, GFP_KERNEL); 688f4fcbbe9SPaul Mackerras if (dp->data == NULL) { 689f4fcbbe9SPaul Mackerras remove_flash_pde(dp); 690f4fcbbe9SPaul Mackerras return -ENOMEM; 691f4fcbbe9SPaul Mackerras } 692f4fcbbe9SPaul Mackerras 693f4fcbbe9SPaul Mackerras /* 694f4fcbbe9SPaul Mackerras * This code assumes that the status int is the first member of the 695f4fcbbe9SPaul Mackerras * struct 696f4fcbbe9SPaul Mackerras */ 697f4fcbbe9SPaul Mackerras status = (int *) dp->data; 698f4fcbbe9SPaul Mackerras token = rtas_token(rtas_call_name); 699f4fcbbe9SPaul Mackerras if (token == RTAS_UNKNOWN_SERVICE) 700f4fcbbe9SPaul Mackerras *status = FLASH_AUTH; 701f4fcbbe9SPaul Mackerras else 702f4fcbbe9SPaul Mackerras *status = FLASH_NO_OP; 703f4fcbbe9SPaul Mackerras 704f4fcbbe9SPaul Mackerras return 0; 705f4fcbbe9SPaul Mackerras } 706f4fcbbe9SPaul Mackerras 707f4fcbbe9SPaul Mackerras static struct proc_dir_entry *create_flash_pde(const char *filename, 7085dfe4c96SArjan van de Ven const struct file_operations *fops) 709f4fcbbe9SPaul Mackerras { 71066747138SDenis V. Lunev return proc_create(filename, S_IRUSR | S_IWUSR, NULL, fops); 711f4fcbbe9SPaul Mackerras } 712f4fcbbe9SPaul Mackerras 7135dfe4c96SArjan van de Ven static const struct file_operations rtas_flash_operations = { 71466747138SDenis V. Lunev .owner = THIS_MODULE, 715f4fcbbe9SPaul Mackerras .read = rtas_flash_read, 716f4fcbbe9SPaul Mackerras .write = rtas_flash_write, 717f4fcbbe9SPaul Mackerras .open = rtas_excl_open, 718f4fcbbe9SPaul Mackerras .release = rtas_flash_release, 719f4fcbbe9SPaul Mackerras }; 720f4fcbbe9SPaul Mackerras 7215dfe4c96SArjan van de Ven static const struct file_operations manage_flash_operations = { 72266747138SDenis V. Lunev .owner = THIS_MODULE, 723f4fcbbe9SPaul Mackerras .read = manage_flash_read, 724f4fcbbe9SPaul Mackerras .write = manage_flash_write, 725f4fcbbe9SPaul Mackerras .open = rtas_excl_open, 726f4fcbbe9SPaul Mackerras .release = rtas_excl_release, 727f4fcbbe9SPaul Mackerras }; 728f4fcbbe9SPaul Mackerras 7295dfe4c96SArjan van de Ven static const struct file_operations validate_flash_operations = { 73066747138SDenis V. Lunev .owner = THIS_MODULE, 731f4fcbbe9SPaul Mackerras .read = validate_flash_read, 732f4fcbbe9SPaul Mackerras .write = validate_flash_write, 733f4fcbbe9SPaul Mackerras .open = rtas_excl_open, 734f4fcbbe9SPaul Mackerras .release = validate_flash_release, 735f4fcbbe9SPaul Mackerras }; 736f4fcbbe9SPaul Mackerras 7371c21a293SMichael Ellerman static int __init rtas_flash_init(void) 738f4fcbbe9SPaul Mackerras { 739f4fcbbe9SPaul Mackerras int rc; 740f4fcbbe9SPaul Mackerras 741f4fcbbe9SPaul Mackerras if (rtas_token("ibm,update-flash-64-and-reboot") == 742f4fcbbe9SPaul Mackerras RTAS_UNKNOWN_SERVICE) { 743f4fcbbe9SPaul Mackerras printk(KERN_ERR "rtas_flash: no firmware flash support\n"); 744f4fcbbe9SPaul Mackerras return 1; 745f4fcbbe9SPaul Mackerras } 746f4fcbbe9SPaul Mackerras 747188917e1SBenjamin Herrenschmidt firmware_flash_pde = create_flash_pde("powerpc/rtas/" 748f4fcbbe9SPaul Mackerras FIRMWARE_FLASH_NAME, 749f4fcbbe9SPaul Mackerras &rtas_flash_operations); 750f4fcbbe9SPaul Mackerras if (firmware_flash_pde == NULL) { 751f4fcbbe9SPaul Mackerras rc = -ENOMEM; 752f4fcbbe9SPaul Mackerras goto cleanup; 753f4fcbbe9SPaul Mackerras } 754f4fcbbe9SPaul Mackerras 755f4fcbbe9SPaul Mackerras rc = initialize_flash_pde_data("ibm,update-flash-64-and-reboot", 756f4fcbbe9SPaul Mackerras sizeof(struct rtas_update_flash_t), 757f4fcbbe9SPaul Mackerras firmware_flash_pde); 758f4fcbbe9SPaul Mackerras if (rc != 0) 759f4fcbbe9SPaul Mackerras goto cleanup; 760f4fcbbe9SPaul Mackerras 761188917e1SBenjamin Herrenschmidt firmware_update_pde = create_flash_pde("powerpc/rtas/" 762f4fcbbe9SPaul Mackerras FIRMWARE_UPDATE_NAME, 763f4fcbbe9SPaul Mackerras &rtas_flash_operations); 764f4fcbbe9SPaul Mackerras if (firmware_update_pde == NULL) { 765f4fcbbe9SPaul Mackerras rc = -ENOMEM; 766f4fcbbe9SPaul Mackerras goto cleanup; 767f4fcbbe9SPaul Mackerras } 768f4fcbbe9SPaul Mackerras 769f4fcbbe9SPaul Mackerras rc = initialize_flash_pde_data("ibm,update-flash-64-and-reboot", 770f4fcbbe9SPaul Mackerras sizeof(struct rtas_update_flash_t), 771f4fcbbe9SPaul Mackerras firmware_update_pde); 772f4fcbbe9SPaul Mackerras if (rc != 0) 773f4fcbbe9SPaul Mackerras goto cleanup; 774f4fcbbe9SPaul Mackerras 775188917e1SBenjamin Herrenschmidt validate_pde = create_flash_pde("powerpc/rtas/" VALIDATE_FLASH_NAME, 776f4fcbbe9SPaul Mackerras &validate_flash_operations); 777f4fcbbe9SPaul Mackerras if (validate_pde == NULL) { 778f4fcbbe9SPaul Mackerras rc = -ENOMEM; 779f4fcbbe9SPaul Mackerras goto cleanup; 780f4fcbbe9SPaul Mackerras } 781f4fcbbe9SPaul Mackerras 782f4fcbbe9SPaul Mackerras rc = initialize_flash_pde_data("ibm,validate-flash-image", 783f4fcbbe9SPaul Mackerras sizeof(struct rtas_validate_flash_t), 784f4fcbbe9SPaul Mackerras validate_pde); 785f4fcbbe9SPaul Mackerras if (rc != 0) 786f4fcbbe9SPaul Mackerras goto cleanup; 787f4fcbbe9SPaul Mackerras 788188917e1SBenjamin Herrenschmidt manage_pde = create_flash_pde("powerpc/rtas/" MANAGE_FLASH_NAME, 789f4fcbbe9SPaul Mackerras &manage_flash_operations); 790f4fcbbe9SPaul Mackerras if (manage_pde == NULL) { 791f4fcbbe9SPaul Mackerras rc = -ENOMEM; 792f4fcbbe9SPaul Mackerras goto cleanup; 793f4fcbbe9SPaul Mackerras } 794f4fcbbe9SPaul Mackerras 795f4fcbbe9SPaul Mackerras rc = initialize_flash_pde_data("ibm,manage-flash-image", 796f4fcbbe9SPaul Mackerras sizeof(struct rtas_manage_flash_t), 797f4fcbbe9SPaul Mackerras manage_pde); 798f4fcbbe9SPaul Mackerras if (rc != 0) 799f4fcbbe9SPaul Mackerras goto cleanup; 800f4fcbbe9SPaul Mackerras 801f4fcbbe9SPaul Mackerras rtas_flash_term_hook = rtas_flash_firmware; 802ae883cabSJohn Rose 803ae883cabSJohn Rose flash_block_cache = kmem_cache_create("rtas_flash_cache", 804ae883cabSJohn Rose RTAS_BLK_SIZE, RTAS_BLK_SIZE, 0, 80520c2df83SPaul Mundt rtas_block_ctor); 806ae883cabSJohn Rose if (!flash_block_cache) { 807ae883cabSJohn Rose printk(KERN_ERR "%s: failed to create block cache\n", 808e48b1b45SHarvey Harrison __func__); 809ae883cabSJohn Rose rc = -ENOMEM; 810ae883cabSJohn Rose goto cleanup; 811ae883cabSJohn Rose } 812f4fcbbe9SPaul Mackerras return 0; 813f4fcbbe9SPaul Mackerras 814f4fcbbe9SPaul Mackerras cleanup: 815f4fcbbe9SPaul Mackerras remove_flash_pde(firmware_flash_pde); 816f4fcbbe9SPaul Mackerras remove_flash_pde(firmware_update_pde); 817f4fcbbe9SPaul Mackerras remove_flash_pde(validate_pde); 818f4fcbbe9SPaul Mackerras remove_flash_pde(manage_pde); 819f4fcbbe9SPaul Mackerras 820f4fcbbe9SPaul Mackerras return rc; 821f4fcbbe9SPaul Mackerras } 822f4fcbbe9SPaul Mackerras 8231c21a293SMichael Ellerman static void __exit rtas_flash_cleanup(void) 824f4fcbbe9SPaul Mackerras { 825f4fcbbe9SPaul Mackerras rtas_flash_term_hook = NULL; 826ae883cabSJohn Rose 827ae883cabSJohn Rose if (flash_block_cache) 828ae883cabSJohn Rose kmem_cache_destroy(flash_block_cache); 829ae883cabSJohn Rose 830f4fcbbe9SPaul Mackerras remove_flash_pde(firmware_flash_pde); 831f4fcbbe9SPaul Mackerras remove_flash_pde(firmware_update_pde); 832f4fcbbe9SPaul Mackerras remove_flash_pde(validate_pde); 833f4fcbbe9SPaul Mackerras remove_flash_pde(manage_pde); 834f4fcbbe9SPaul Mackerras } 835f4fcbbe9SPaul Mackerras 836f4fcbbe9SPaul Mackerras module_init(rtas_flash_init); 837f4fcbbe9SPaul Mackerras module_exit(rtas_flash_cleanup); 838f4fcbbe9SPaul Mackerras MODULE_LICENSE("GPL"); 839