nvram_64.c revision e49e2e87235518c21b1f5228809209831e6169e7
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * c 2001 PPC 64 Team, IBM Corp 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * modify it under the terms of the GNU General Public License 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as published by the Free Software Foundation; either version 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 2 of the License, or (at your option) any later version. 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * /dev/nvram driver for PPC64 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This perhaps should live in drivers/char 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TODO: Split the /dev/nvram part (that one can use 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * drivers/char/generic_nvram.c) from the arch & partition 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * parsing code. 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h> 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fcntl.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/nvram.h> 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h> 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/nvram.h> 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/rtas.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/prom.h> 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/machdep.h> 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG_NVRAM 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3736673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt#define NVRAM_HEADER_LEN sizeof(struct nvram_header) 3836673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt#define NVRAM_BLOCK_LEN NVRAM_HEADER_LEN 3936673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt#define NVRAM_MAX_REQ 2079 4036673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt#define NVRAM_MIN_REQ 1055 4174d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt 4274d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt/* If change this size, then change the size of NVNAME_LEN */ 4374d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidtstruct nvram_header { 4474d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt unsigned char signature; 4574d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt unsigned char checksum; 4674d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt unsigned short length; 4774d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt char name[12]; 4874d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt}; 4974d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt 5074d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidtstruct nvram_partition { 5174d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt struct list_head partition; 5274d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt struct nvram_header header; 5374d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt unsigned int index; 5474d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt}; 5574d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct nvram_partition * nvram_part; 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic long nvram_error_log_index = -1; 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic long nvram_error_log_size = 0; 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct err_log_info { 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int error_type; 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int seq_num; 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin) 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int size; 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ppc_md.nvram_size == NULL) 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = ppc_md.nvram_size(); 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (origin) { 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 1: 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset += file->f_pos; 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 2: 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset += size; 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (offset < 0) 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds file->f_pos = offset; 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return file->f_pos; 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t dev_nvram_read(struct file *file, char __user *buf, 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t count, loff_t *ppos) 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 91f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ssize_t ret; 92f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann char *tmp = NULL; 93f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ssize_t size; 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 95f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = -ENODEV; 96f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (!ppc_md.nvram_size) 97f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 98f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann 99f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = 0; 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = ppc_md.nvram_size(); 101f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (*ppos >= size || size < 0) 102f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 104f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann count = min_t(size_t, count, size - *ppos); 105f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann count = min(count, PAGE_SIZE); 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 107f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = -ENOMEM; 108f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann tmp = kmalloc(count, GFP_KERNEL); 109f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (!tmp) 110f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 112f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = ppc_md.nvram_read(tmp, count, ppos); 113f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (ret <= 0) 114f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 115f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann 116f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (copy_to_user(buf, tmp, ret)) 117f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = -EFAULT; 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 119f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmannout: 120f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann kfree(tmp); 121f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann return ret; 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t dev_nvram_write(struct file *file, const char __user *buf, 126f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann size_t count, loff_t *ppos) 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 128f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ssize_t ret; 129f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann char *tmp = NULL; 130f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ssize_t size; 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 132f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = -ENODEV; 133f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (!ppc_md.nvram_size) 134f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 135f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann 136f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = 0; 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = ppc_md.nvram_size(); 138f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (*ppos >= size || size < 0) 139f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 141f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann count = min_t(size_t, count, size - *ppos); 142f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann count = min(count, PAGE_SIZE); 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 144f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = -ENOMEM; 145f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann tmp = kmalloc(count, GFP_KERNEL); 146f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (!tmp) 147f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 148f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann 149f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = -EFAULT; 150f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann if (copy_from_user(tmp, buf, count)) 151f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 152f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann 153f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ret = ppc_md.nvram_write(tmp, count, ppos); 154f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann 155f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmannout: 156f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann kfree(tmp); 157f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann return ret; 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1613b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixnerstatic long dev_nvram_ioctl(struct file *file, unsigned int cmd, 1623b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner unsigned long arg) 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch(cmd) { 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_PPC_PMAC 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case OBSOLETE_PMAC_NVRAM_GET_OFFSET: 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n"); 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case IOC_NVRAM_GET_OFFSET: { 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int part, offset; 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 171e8222502ee6157e2713da9e0792c21f4ad458d50Benjamin Herrenschmidt if (!machine_is(powermac)) 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0) 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (part < pmac_nvram_OF || part > pmac_nvram_NR) 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset = pmac_get_partition(part); 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (offset < 0) 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return offset; 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0) 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* CONFIG_PPC_PMAC */ 185af308377e204e25f1f58627d05fe0f483703b514Stephen Rothwell default: 186af308377e204e25f1f58627d05fe0f483703b514Stephen Rothwell return -EINVAL; 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1905dfe4c964a0dd7bb3a1d64a4166835a153146207Arjan van de Venconst struct file_operations nvram_fops = { 1913b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner .owner = THIS_MODULE, 1923b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner .llseek = dev_nvram_llseek, 1933b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner .read = dev_nvram_read, 1943b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner .write = dev_nvram_write, 1953b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner .unlocked_ioctl = dev_nvram_ioctl, 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice nvram_dev = { 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds NVRAM_MINOR, 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "nvram", 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds &nvram_fops 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_NVRAM 20632c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic void __init nvram_print_partitions(char * label) 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct list_head * p; 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nvram_partition * tmp_part; 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "--------%s---------\n", label); 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n"); 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_for_each(p, &nvram_part->partition) { 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part = list_entry(p, struct nvram_partition, partition); 2155a43ee65620d628ba04deecf241b63b2410b97f2Will Schmidt printk(KERN_WARNING "%4d \t%02x\t%02x\t%d\t%s\n", 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part->index, tmp_part->header.signature, 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part->header.checksum, tmp_part->header.length, 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part->header.name); 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 22432c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic int __init nvram_write_header(struct nvram_partition * part) 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t tmp_index; 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_index = part->index; 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = ppc_md.nvram_write((char *)&part->header, NVRAM_HEADER_LEN, &tmp_index); 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 23632c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic unsigned char __init nvram_checksum(struct nvram_header *p) 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int c_sum, c_sum2; 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned short *sp = (unsigned short *)p->name; /* assume 6 shorts */ 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum = p->signature + p->length + sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5]; 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* The sum may have spilled into the 3rd byte. Fold it back. */ 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff; 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* The sum cannot exceed 2 bytes. Fold it into a checksum */ 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum2 = (c_sum >> 8) + (c_sum << 8); 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum = ((c_sum + c_sum2) >> 8) & 0xff; 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return c_sum; 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 25032c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic int __init nvram_remove_os_partition(void) 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct list_head *i; 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct list_head *j; 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nvram_partition * part; 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nvram_partition * cur_part; 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_for_each(i, &nvram_part->partition) { 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part = list_entry(i, struct nvram_partition, partition); 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (part->header.signature != NVRAM_SIG_OS) 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Make os partition a free partition */ 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->header.signature = NVRAM_SIG_FREE; 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sprintf(part->header.name, "wwwwwwwwwwww"); 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->header.checksum = nvram_checksum(&part->header); 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Merge contiguous free partitions backwards */ 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_for_each_prev(j, &part->partition) { 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_part = list_entry(j, struct nvram_partition, partition); 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) { 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->header.length += cur_part->header.length; 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->header.checksum = nvram_checksum(&part->header); 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->index = cur_part->index; 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_del(&cur_part->partition); 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(cur_part); 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds j = &part->partition; /* fixup our loop */ 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Merge contiguous free partitions forwards */ 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_for_each(j, &part->partition) { 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_part = list_entry(j, struct nvram_partition, partition); 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) { 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->header.length += cur_part->header.length; 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->header.checksum = nvram_checksum(&part->header); 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_del(&cur_part->partition); 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(cur_part); 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds j = &part->partition; /* fixup our loop */ 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = nvram_write_header(part); 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_remove_os_partition: nvram_write failed (%d)\n", rc); 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3104e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt/** 3114e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * nvram_create_partition - Create a partition in nvram 3124e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @name: name of the partition to create 3134e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @sig: signature of the partition to create 31436673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt * @req_size: size of data to allocate in bytes 3154e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @min_size: minimum acceptable size (0 means req_size) 316e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * 317e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * Returns a negative error code or a positive nvram index 318e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * of the beginning of the data area of the newly created 319e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * partition. If you provided a min_size smaller than req_size 320e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * you need to query for the actual size yourself after the 321e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * call using nvram_partition_get_size(). 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 323e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidtstatic loff_t __init nvram_create_partition(const char *name, int sig, 324e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt int req_size, int min_size) 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 326a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann struct nvram_partition *part; 327a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann struct nvram_partition *new_part; 3280339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton struct nvram_partition *free_part = NULL; 329cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt static char nv_init_vals[16]; 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t tmp_index; 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds long size = 0; 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 3334e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt 33436673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt /* Convert sizes from bytes to blocks */ 33536673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt req_size = _ALIGN_UP(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN; 33636673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt min_size = _ALIGN_UP(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN; 33736673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt 3384e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt /* If no minimum size specified, make it the same as the 3394e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * requested size 3404e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt */ 3414e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt if (min_size == 0) 3424e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt min_size = req_size; 343e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (min_size > req_size) 344e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return -EINVAL; 3454e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt 34636673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt /* Now add one block to each for the header */ 34736673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt req_size += 1; 34836673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt min_size += 1; 34936673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find a free partition that will give us the maximum needed size 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds If can't find one that will give us the minimum size needed */ 352a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann list_for_each_entry(part, &nvram_part->partition, partition) { 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (part->header.signature != NVRAM_SIG_FREE) 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3564e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt if (part->header.length >= req_size) { 3574e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt size = req_size; 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_part = part; 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3614e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt if (part->header.length > size && 3624e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt part->header.length >= min_size) { 3634e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt size = part->header.length; 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_part = part; 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3670339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton if (!size) 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOSPC; 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Create our OS partition */ 3710339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton new_part = kmalloc(sizeof(*new_part), GFP_KERNEL); 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!new_part) { 373e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt pr_err("nvram_create_os_partition: kmalloc failed\n"); 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds new_part->index = free_part->index; 3784e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt new_part->header.signature = sig; 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds new_part->header.length = size; 3804e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt strncpy(new_part->header.name, name, 12); 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds new_part->header.checksum = nvram_checksum(&new_part->header); 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = nvram_write_header(new_part); 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 385e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt pr_err("nvram_create_os_partition: nvram_write_header " 386e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt "failed (%d)\n", rc); 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 389e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt list_add_tail(&new_part->partition, &free_part->partition); 390e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt 391e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt /* Adjust or remove the partition we stole the space from */ 392e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (free_part->header.length > size) { 393e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt free_part->index += size * NVRAM_BLOCK_LEN; 394e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt free_part->header.length -= size; 395e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt free_part->header.checksum = nvram_checksum(&free_part->header); 396e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt rc = nvram_write_header(free_part); 397e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (rc <= 0) { 398e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt pr_err("nvram_create_os_partition: nvram_write_header " 399e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt "failed (%d)\n", rc); 400e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return rc; 401e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt } 402e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt } else { 403e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt list_del(&free_part->partition); 404e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt kfree(free_part); 405e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt } 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 407e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt /* Clear the new partition */ 408cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt for (tmp_index = new_part->index + NVRAM_HEADER_LEN; 409cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt tmp_index < ((size - 1) * NVRAM_BLOCK_LEN); 410cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt tmp_index += NVRAM_BLOCK_LEN) { 411cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt rc = ppc_md.nvram_write(nv_init_vals, NVRAM_BLOCK_LEN, &tmp_index); 412cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt if (rc <= 0) { 413cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt pr_err("nvram_create_partition: nvram_write failed (%d)\n", rc); 414cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt return rc; 415cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt } 4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 418e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return new_part->index + NVRAM_HEADER_LEN; 419e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt} 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 421e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt/** 422e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * nvram_get_partition_size - Get the data size of an nvram partition 423e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * @data_index: This is the offset of the start of the data of 424e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * the partition. The same value that is returned by 425e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * nvram_create_partition(). 426e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt */ 427e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidtstatic int nvram_get_partition_size(loff_t data_index) 428e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt{ 429e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt struct nvram_partition *part; 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 431e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt list_for_each_entry(part, &nvram_part->partition, partition) { 432e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (part->index + NVRAM_HEADER_LEN == data_index) 433e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return (part->header.length - 1) * NVRAM_BLOCK_LEN; 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 435e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return -1; 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* nvram_setup_partition 4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This will setup the partition we need for buffering the 4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * error logs and cleanup partitions if needed. 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The general strategy is the following: 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 1.) If there is ppc64,linux partition large enough then use it. 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 2.) If there is not a ppc64,linux partition large enough, search 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for a free partition that is large enough. 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3.) If there is not a free partition large enough remove 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * _all_ OS partitions and consolidate the space. 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4.) Will first try getting a chunk that will satisfy the maximum 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * error log size (NVRAM_MAX_REQ). 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 5.) If the max chunk cannot be allocated then try finding a chunk 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * that will satisfy the minum needed (NVRAM_MIN_REQ). 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 45532c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic int __init nvram_setup_partition(void) 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct list_head * p; 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nvram_partition * part; 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* For now, we don't do any of this on pmac, until I 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * have figured out if it's worth killing some unused stuffs 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in our nvram, as Apple defined partitions use pretty much 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * all of the space 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 466e8222502ee6157e2713da9e0792c21f4ad458d50Benjamin Herrenschmidt if (machine_is(powermac)) 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOSPC; 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* see if we have an OS partition that meets our needs. 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds will try getting the max we need. If not we'll delete 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds partitions and try again. */ 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_for_each(p, &nvram_part->partition) { 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part = list_entry(p, struct nvram_partition, partition); 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (part->header.signature != NVRAM_SIG_OS) 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (strcmp(part->header.name, "ppc64,linux")) 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 48036673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt if ((part->header.length - 1) * NVRAM_BLOCK_LEN >= NVRAM_MIN_REQ) { 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* found our partition */ 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nvram_error_log_index = part->index + NVRAM_HEADER_LEN; 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nvram_error_log_size = ((part->header.length - 1) * 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds NVRAM_BLOCK_LEN) - sizeof(struct err_log_info); 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* try creating a partition with the free space we have */ 490e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt rc = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS, 4914e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt NVRAM_MAX_REQ, NVRAM_MIN_REQ); 492e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (rc < 0) { 493e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt /* need to free up some space */ 494e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt rc = nvram_remove_os_partition(); 495e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (rc) 496e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return rc; 497e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt /* create a partition in this new space */ 498e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt rc = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS, 499e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt NVRAM_MAX_REQ, NVRAM_MIN_REQ); 500e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (rc < 0) { 501e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt pr_err("nvram_create_partition: Could not find" 502e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt " enough space in NVRAM for partition\n"); 503e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return rc; 504e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt } 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 507e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt nvram_error_log_index = rc; 508e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt nvram_error_log_size = nvram_get_partition_size(rc) - sizeof(struct err_log_info); 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 51232c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic int __init nvram_scan_partitions(void) 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t cur_index = 0; 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nvram_header phead; 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nvram_partition * tmp_part; 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char c_sum; 5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char * header; 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int total_size; 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int err; 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ppc_md.nvram_size == NULL) 5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds total_size = ppc_md.nvram_size(); 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5265cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day header = kmalloc(NVRAM_HEADER_LEN, GFP_KERNEL); 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!header) { 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_scan_partitions: Failed kmalloc\n"); 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (cur_index < total_size) { 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = ppc_md.nvram_read(header, NVRAM_HEADER_LEN, &cur_index); 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (err != NVRAM_HEADER_LEN) { 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_scan_partitions: Error parsing " 5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "nvram partitions\n"); 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_index -= NVRAM_HEADER_LEN; /* nvram_read will advance us */ 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&phead, header, NVRAM_HEADER_LEN); 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = 0; 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum = nvram_checksum(&phead); 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (c_sum != phead.checksum) { 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "WARNING: nvram partition checksum" 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds " was %02x, should be %02x!\n", 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds phead.checksum, c_sum); 5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "Terminating nvram partition scan\n"); 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!phead.length) { 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "WARNING: nvram corruption " 5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "detected: 0-length partition\n"); 5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part = (struct nvram_partition *) 5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kmalloc(sizeof(struct nvram_partition), GFP_KERNEL); 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = -ENOMEM; 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!tmp_part) { 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_scan_partitions: kmalloc failed\n"); 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN); 5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part->index = cur_index; 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_add_tail(&tmp_part->partition, &nvram_part->partition); 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_index += phead.length * NVRAM_BLOCK_LEN; 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = 0; 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out: 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(header); 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return err; 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init nvram_init(void) 5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int error; 5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 585578914cffc283b907777796420148d582072cbaeBenjamin Herrenschmidt BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16); 586578914cffc283b907777796420148d582072cbaeBenjamin Herrenschmidt 5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0) 5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = misc_register(&nvram_dev); 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc != 0) { 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_init: failed to register device\n"); 5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* initialize our anchor for the nvram partition list */ 5975cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day nvram_part = kmalloc(sizeof(struct nvram_partition), GFP_KERNEL); 5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!nvram_part) { 5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_init: Failed kmalloc\n"); 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds INIT_LIST_HEAD(&nvram_part->partition); 6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Get all the NVRAM partitions */ 6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds error = nvram_scan_partitions(); 6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (error) { 6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_init: Failed nvram_scan_partitions\n"); 6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return error; 6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(nvram_setup_partition()) 6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "nvram_init: Could not find nvram partition" 6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds " for nvram buffered error logging.\n"); 6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_NVRAM 6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nvram_print_partitions("NVRAM Partitions"); 6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid __exit nvram_cleanup(void) 6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds misc_deregister( &nvram_dev ); 6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_PPC_PSERIES 6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* nvram_write_error_log 6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We need to buffer the error logs into nvram to ensure that we have 6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the failure information to decode. If we have a severe error there 6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * is no way to guarantee that the OS or the machine is in a state to 6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * get back to user land and write the error to disk. For example if 6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the SCSI device driver causes a Machine Check by writing to a bad 6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * IO address, there is no way of guaranteeing that the device driver 6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * is in any state that is would also be able to write the error data 6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * captured to disk, thus we buffer it in NVRAM for analysis on the 6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * next boot. 6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * In NVRAM the partition containing the error log buffer will looks like: 6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Header (in bytes): 6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * +-----------+----------+--------+------------+------------------+ 6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * | signature | checksum | length | name | data | 6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * |0 |1 |2 3|4 15|16 length-1| 6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * +-----------+----------+--------+------------+------------------+ 6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The 'data' section would look like (in bytes): 6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * +--------------+------------+-----------------------------------+ 6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * | event_logged | sequence # | error log | 6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * |0 3|4 7|8 nvram_error_log_size-1| 6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * +--------------+------------+-----------------------------------+ 6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * event_logged: 0 if event has not been logged to syslog, 1 if it has 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * sequence #: The unique sequence # for each event. (until it wraps) 6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * error log: The error log from event_scan 6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 6590f2342c85df4248bc1cd72421b13969a0782ed6aLinas Vepstasint nvram_write_error_log(char * buff, int length, 6600f2342c85df4248bc1cd72421b13969a0782ed6aLinas Vepstas unsigned int err_type, unsigned int error_log_cnt) 6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t tmp_index; 6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct err_log_info info; 6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (nvram_error_log_index == -1) { 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ESPIPE; 6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (length > nvram_error_log_size) { 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds length = nvram_error_log_size; 6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds info.error_type = err_type; 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds info.seq_num = error_log_cnt; 6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_index = nvram_error_log_index; 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index); 6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); 6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = ppc_md.nvram_write(buff, length, &tmp_index); 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* nvram_read_error_log 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Reads nvram for error log for at most 'length' 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 6980f2342c85df4248bc1cd72421b13969a0782ed6aLinas Vepstasint nvram_read_error_log(char * buff, int length, 6990f2342c85df4248bc1cd72421b13969a0782ed6aLinas Vepstas unsigned int * err_type, unsigned int * error_log_cnt) 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t tmp_index; 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct err_log_info info; 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (nvram_error_log_index == -1) 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -1; 7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (length > nvram_error_log_size) 7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds length = nvram_error_log_size; 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_index = nvram_error_log_index; 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index); 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc); 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = ppc_md.nvram_read(buff, length, &tmp_index); 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc); 7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7250f2342c85df4248bc1cd72421b13969a0782ed6aLinas Vepstas *error_log_cnt = info.seq_num; 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *err_type = info.error_type; 7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This doesn't actually zero anything, but it sets the event_logged 7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * word to tell that this event is safely in syslog. 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint nvram_clear_error_log(void) 7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t tmp_index; 7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int clear_word = ERR_FLAG_ALREADY_LOGGED; 7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 740fd62c6c448669a946e94fbb0ad179918b2233e3dThomas Gleixner if (nvram_error_log_index == -1) 741fd62c6c448669a946e94fbb0ad179918b2233e3dThomas Gleixner return -1; 742fd62c6c448669a946e94fbb0ad179918b2233e3dThomas Gleixner 7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_index = nvram_error_log_index; 7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index); 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc); 7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* CONFIG_PPC_PSERIES */ 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(nvram_init); 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(nvram_cleanup); 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 759