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 3974d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt 4074d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt/* If change this size, then change the size of NVNAME_LEN */ 4174d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidtstruct nvram_header { 4274d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt unsigned char signature; 4374d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt unsigned char checksum; 4474d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt unsigned short length; 456024ede9ba84aa1b891c2d6bc98eda07801235e5Jim Keniston /* Terminating null required only for names < 12 chars. */ 4674d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt char name[12]; 4774d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt}; 4874d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt 4974d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidtstruct nvram_partition { 5074d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt struct list_head partition; 5174d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt struct nvram_header header; 5274d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt unsigned int index; 5374d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt}; 5474d51d029818eca9d1aec22dd2895e269c0044b1Benjamin Herrenschmidt 55690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Kenistonstatic LIST_HEAD(nvram_partitions); 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin) 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int size; 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ppc_md.nvram_size == NULL) 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = ppc_md.nvram_size(); 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (origin) { 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 1: 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset += file->f_pos; 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 2: 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset += size; 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (offset < 0) 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds file->f_pos = offset; 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return file->f_pos; 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t dev_nvram_read(struct file *file, char __user *buf, 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t count, loff_t *ppos) 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 83f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ssize_t ret; 84f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann char *tmp = NULL; 85f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann ssize_t size; 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 877029705a9d0544186c29ae09708b3e5adb512835Chen Gang if (!ppc_md.nvram_size) { 887029705a9d0544186c29ae09708b3e5adb512835Chen Gang ret = -ENODEV; 89f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 907029705a9d0544186c29ae09708b3e5adb512835Chen Gang } 91f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = ppc_md.nvram_size(); 937029705a9d0544186c29ae09708b3e5adb512835Chen Gang if (size < 0) { 947029705a9d0544186c29ae09708b3e5adb512835Chen Gang ret = size; 95f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 967029705a9d0544186c29ae09708b3e5adb512835Chen Gang } 977029705a9d0544186c29ae09708b3e5adb512835Chen Gang 987029705a9d0544186c29ae09708b3e5adb512835Chen Gang if (*ppos >= size) { 997029705a9d0544186c29ae09708b3e5adb512835Chen Gang ret = 0; 1007029705a9d0544186c29ae09708b3e5adb512835Chen Gang goto out; 1017029705a9d0544186c29ae09708b3e5adb512835Chen Gang } 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 103f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann count = min_t(size_t, count, size - *ppos); 104f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann count = min(count, PAGE_SIZE); 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 106f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann tmp = kmalloc(count, GFP_KERNEL); 1077029705a9d0544186c29ae09708b3e5adb512835Chen Gang if (!tmp) { 1087029705a9d0544186c29ae09708b3e5adb512835Chen Gang ret = -ENOMEM; 109f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann goto out; 1107029705a9d0544186c29ae09708b3e5adb512835Chen Gang } 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 nvram_partition * tmp_part; 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "--------%s---------\n", label); 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n"); 212690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston list_for_each_entry(tmp_part, &nvram_partitions, partition) { 213e0513d9ea8dd2a7b21b699ada8d59d8afbb8b5b7Chen Gang printk(KERN_WARNING "%4d \t%02x\t%02x\t%d\t%12.12s\n", 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part->index, tmp_part->header.signature, 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part->header.checksum, tmp_part->header.length, 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part->header.name); 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 22232c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic int __init nvram_write_header(struct nvram_partition * part) 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t tmp_index; 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 226c81095a465b2c1cd819fb14ee3cd07bc1b377af1Cedric Le Goater struct nvram_header phead; 227c81095a465b2c1cd819fb14ee3cd07bc1b377af1Cedric Le Goater 228c81095a465b2c1cd819fb14ee3cd07bc1b377af1Cedric Le Goater memcpy(&phead, &part->header, NVRAM_HEADER_LEN); 229c81095a465b2c1cd819fb14ee3cd07bc1b377af1Cedric Le Goater phead.length = cpu_to_be16(phead.length); 230c81095a465b2c1cd819fb14ee3cd07bc1b377af1Cedric Le Goater 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_index = part->index; 232c81095a465b2c1cd819fb14ee3cd07bc1b377af1Cedric Le Goater rc = ppc_md.nvram_write((char *)&phead, NVRAM_HEADER_LEN, &tmp_index); 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 23832c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic unsigned char __init nvram_checksum(struct nvram_header *p) 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int c_sum, c_sum2; 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned short *sp = (unsigned short *)p->name; /* assume 6 shorts */ 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum = p->signature + p->length + sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5]; 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* The sum may have spilled into the 3rd byte. Fold it back. */ 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff; 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* The sum cannot exceed 2 bytes. Fold it into a checksum */ 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum2 = (c_sum >> 8) + (c_sum << 8); 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum = ((c_sum + c_sum2) >> 8) & 0xff; 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return c_sum; 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2520f4ac132365e56802cbe377313491aa84086371cJim Keniston/* 2530f4ac132365e56802cbe377313491aa84086371cJim Keniston * Per the criteria passed via nvram_remove_partition(), should this 2540f4ac132365e56802cbe377313491aa84086371cJim Keniston * partition be removed? 1=remove, 0=keep 2550f4ac132365e56802cbe377313491aa84086371cJim Keniston */ 2560f4ac132365e56802cbe377313491aa84086371cJim Kenistonstatic int nvram_can_remove_partition(struct nvram_partition *part, 2570f4ac132365e56802cbe377313491aa84086371cJim Keniston const char *name, int sig, const char *exceptions[]) 2580f4ac132365e56802cbe377313491aa84086371cJim Keniston{ 2590f4ac132365e56802cbe377313491aa84086371cJim Keniston if (part->header.signature != sig) 2600f4ac132365e56802cbe377313491aa84086371cJim Keniston return 0; 2610f4ac132365e56802cbe377313491aa84086371cJim Keniston if (name) { 2620f4ac132365e56802cbe377313491aa84086371cJim Keniston if (strncmp(name, part->header.name, 12)) 2630f4ac132365e56802cbe377313491aa84086371cJim Keniston return 0; 2640f4ac132365e56802cbe377313491aa84086371cJim Keniston } else if (exceptions) { 2650f4ac132365e56802cbe377313491aa84086371cJim Keniston const char **except; 2660f4ac132365e56802cbe377313491aa84086371cJim Keniston for (except = exceptions; *except; except++) { 2670f4ac132365e56802cbe377313491aa84086371cJim Keniston if (!strncmp(*except, part->header.name, 12)) 2680f4ac132365e56802cbe377313491aa84086371cJim Keniston return 0; 2690f4ac132365e56802cbe377313491aa84086371cJim Keniston } 2700f4ac132365e56802cbe377313491aa84086371cJim Keniston } 2710f4ac132365e56802cbe377313491aa84086371cJim Keniston return 1; 2720f4ac132365e56802cbe377313491aa84086371cJim Keniston} 2730f4ac132365e56802cbe377313491aa84086371cJim Keniston 274fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt/** 275fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * nvram_remove_partition - Remove one or more partitions in nvram 276fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * @name: name of the partition to remove, or NULL for a 277fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * signature only match 278fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * @sig: signature of the partition(s) to remove 2790f4ac132365e56802cbe377313491aa84086371cJim Keniston * @exceptions: When removing all partitions with a matching signature, 2800f4ac132365e56802cbe377313491aa84086371cJim Keniston * leave these alone. 281fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt */ 282fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt 2830f4ac132365e56802cbe377313491aa84086371cJim Kenistonint __init nvram_remove_partition(const char *name, int sig, 2840f4ac132365e56802cbe377313491aa84086371cJim Keniston const char *exceptions[]) 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 286fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt struct nvram_partition *part, *prev, *tmp; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 289690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston list_for_each_entry(part, &nvram_partitions, partition) { 2900f4ac132365e56802cbe377313491aa84086371cJim Keniston if (!nvram_can_remove_partition(part, name, sig, exceptions)) 291fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt continue; 292fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt 293fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt /* Make partition a free partition */ 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->header.signature = NVRAM_SIG_FREE; 2956024ede9ba84aa1b891c2d6bc98eda07801235e5Jim Keniston strncpy(part->header.name, "wwwwwwwwwwww", 12); 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds part->header.checksum = nvram_checksum(&part->header); 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = nvram_write_header(part); 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 299fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc); 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 302fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt } 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 304fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt /* Merge contiguous ones */ 305fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt prev = NULL; 306690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston list_for_each_entry_safe(part, tmp, &nvram_partitions, partition) { 307fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt if (part->header.signature != NVRAM_SIG_FREE) { 308fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt prev = NULL; 309fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt continue; 310fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt } 311fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt if (prev) { 312fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt prev->header.length += part->header.length; 313fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt prev->header.checksum = nvram_checksum(&part->header); 314fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt rc = nvram_write_header(part); 315fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt if (rc <= 0) { 316fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc); 317fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt return rc; 318fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt } 319fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt list_del(&part->partition); 320fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt kfree(part); 321fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt } else 322fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt prev = part; 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3284e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt/** 3294e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * nvram_create_partition - Create a partition in nvram 3304e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @name: name of the partition to create 3314e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @sig: signature of the partition to create 33236673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt * @req_size: size of data to allocate in bytes 3334e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @min_size: minimum acceptable size (0 means req_size) 334e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * 335e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * Returns a negative error code or a positive nvram index 336e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * of the beginning of the data area of the newly created 337e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * partition. If you provided a min_size smaller than req_size 338e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * you need to query for the actual size yourself after the 339e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * call using nvram_partition_get_size(). 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 341edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtloff_t __init nvram_create_partition(const char *name, int sig, 342edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt int req_size, int min_size) 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 344a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann struct nvram_partition *part; 345a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann struct nvram_partition *new_part; 3460339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton struct nvram_partition *free_part = NULL; 347cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt static char nv_init_vals[16]; 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t tmp_index; 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds long size = 0; 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 3514e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt 35236673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt /* Convert sizes from bytes to blocks */ 35336673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt req_size = _ALIGN_UP(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN; 35436673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt min_size = _ALIGN_UP(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN; 35536673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt 3564e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt /* If no minimum size specified, make it the same as the 3574e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * requested size 3584e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt */ 3594e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt if (min_size == 0) 3604e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt min_size = req_size; 361e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (min_size > req_size) 362e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return -EINVAL; 3634e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt 36436673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt /* Now add one block to each for the header */ 36536673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt req_size += 1; 36636673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt min_size += 1; 36736673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find a free partition that will give us the maximum needed size 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds If can't find one that will give us the minimum size needed */ 370690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston list_for_each_entry(part, &nvram_partitions, partition) { 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (part->header.signature != NVRAM_SIG_FREE) 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3744e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt if (part->header.length >= req_size) { 3754e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt size = req_size; 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_part = part; 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3794e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt if (part->header.length > size && 3804e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt part->header.length >= min_size) { 3814e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt size = part->header.length; 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_part = part; 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3850339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton if (!size) 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOSPC; 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Create our OS partition */ 3890339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton new_part = kmalloc(sizeof(*new_part), GFP_KERNEL); 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!new_part) { 391e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt pr_err("nvram_create_os_partition: kmalloc failed\n"); 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds new_part->index = free_part->index; 3964e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt new_part->header.signature = sig; 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds new_part->header.length = size; 3984e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt strncpy(new_part->header.name, name, 12); 3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds new_part->header.checksum = nvram_checksum(&new_part->header); 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = nvram_write_header(new_part); 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc <= 0) { 403e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt pr_err("nvram_create_os_partition: nvram_write_header " 404e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt "failed (%d)\n", rc); 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 407e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt list_add_tail(&new_part->partition, &free_part->partition); 408e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt 409e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt /* Adjust or remove the partition we stole the space from */ 410e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (free_part->header.length > size) { 411e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt free_part->index += size * NVRAM_BLOCK_LEN; 412e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt free_part->header.length -= size; 413e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt free_part->header.checksum = nvram_checksum(&free_part->header); 414e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt rc = nvram_write_header(free_part); 415e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (rc <= 0) { 416e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt pr_err("nvram_create_os_partition: nvram_write_header " 417e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt "failed (%d)\n", rc); 418e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return rc; 419e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt } 420e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt } else { 421e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt list_del(&free_part->partition); 422e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt kfree(free_part); 423e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt } 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 425e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt /* Clear the new partition */ 426cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt for (tmp_index = new_part->index + NVRAM_HEADER_LEN; 427cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt tmp_index < ((size - 1) * NVRAM_BLOCK_LEN); 428cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt tmp_index += NVRAM_BLOCK_LEN) { 429cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt rc = ppc_md.nvram_write(nv_init_vals, NVRAM_BLOCK_LEN, &tmp_index); 430cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt if (rc <= 0) { 431cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt pr_err("nvram_create_partition: nvram_write failed (%d)\n", rc); 432cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt return rc; 433cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt } 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 436e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return new_part->index + NVRAM_HEADER_LEN; 437e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt} 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 439e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt/** 440e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * nvram_get_partition_size - Get the data size of an nvram partition 441e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * @data_index: This is the offset of the start of the data of 442e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * the partition. The same value that is returned by 443e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * nvram_create_partition(). 444e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt */ 445edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtint nvram_get_partition_size(loff_t data_index) 446e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt{ 447e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt struct nvram_partition *part; 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 449690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston list_for_each_entry(part, &nvram_partitions, partition) { 450e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt if (part->index + NVRAM_HEADER_LEN == data_index) 451e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return (part->header.length - 1) * NVRAM_BLOCK_LEN; 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 453e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt return -1; 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 457cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt/** 458cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * nvram_find_partition - Find an nvram partition by signature and name 459cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @name: Name of the partition or NULL for any name 460cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @sig: Signature to test against 461cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @out_size: if non-NULL, returns the size of the data part of the partition 462cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt */ 463cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidtloff_t nvram_find_partition(const char *name, int sig, int *out_size) 464cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt{ 465cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt struct nvram_partition *p; 466cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt 467690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston list_for_each_entry(p, &nvram_partitions, partition) { 468cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt if (p->header.signature == sig && 469cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt (!name || !strncmp(p->header.name, name, 12))) { 470cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt if (out_size) 471cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt *out_size = (p->header.length - 1) * 472cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt NVRAM_BLOCK_LEN; 473cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt return p->index + NVRAM_HEADER_LEN; 474cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt } 475cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt } 476cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt return 0; 477cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt} 478cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt 479edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtint __init nvram_scan_partitions(void) 4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t cur_index = 0; 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nvram_header phead; 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nvram_partition * tmp_part; 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char c_sum; 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char * header; 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int total_size; 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int err; 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 489edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0) 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds total_size = ppc_md.nvram_size(); 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4935cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day header = kmalloc(NVRAM_HEADER_LEN, GFP_KERNEL); 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!header) { 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_scan_partitions: Failed kmalloc\n"); 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (cur_index < total_size) { 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = ppc_md.nvram_read(header, NVRAM_HEADER_LEN, &cur_index); 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (err != NVRAM_HEADER_LEN) { 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_scan_partitions: Error parsing " 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "nvram partitions\n"); 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_index -= NVRAM_HEADER_LEN; /* nvram_read will advance us */ 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&phead, header, NVRAM_HEADER_LEN); 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 512c81095a465b2c1cd819fb14ee3cd07bc1b377af1Cedric Le Goater phead.length = be16_to_cpu(phead.length); 513c81095a465b2c1cd819fb14ee3cd07bc1b377af1Cedric Le Goater 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = 0; 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c_sum = nvram_checksum(&phead); 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (c_sum != phead.checksum) { 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "WARNING: nvram partition checksum" 5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds " was %02x, should be %02x!\n", 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds phead.checksum, c_sum); 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "Terminating nvram partition scan\n"); 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!phead.length) { 5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "WARNING: nvram corruption " 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "detected: 0-length partition\n"); 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5286e51c9ff6a5f37c4baf3dfab579e8aed33b8f427Zhang Yanfei tmp_part = kmalloc(sizeof(struct nvram_partition), GFP_KERNEL); 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = -ENOMEM; 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!tmp_part) { 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_scan_partitions: kmalloc failed\n"); 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN); 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp_part->index = cur_index; 537690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston list_add_tail(&tmp_part->partition, &nvram_partitions); 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_index += phead.length * NVRAM_BLOCK_LEN; 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = 0; 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 543edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt#ifdef DEBUG_NVRAM 544edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt nvram_print_partitions("NVRAM Partitions"); 545edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt#endif 546edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out: 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(header); 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return err; 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init nvram_init(void) 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 556578914cffc283b907777796420148d582072cbaeBenjamin Herrenschmidt BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16); 557578914cffc283b907777796420148d582072cbaeBenjamin Herrenschmidt 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0) 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds rc = misc_register(&nvram_dev); 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rc != 0) { 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "nvram_init: failed to register device\n"); 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return rc; 5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 570e51df2c170efaeadce4d416e1825b0830de0a795Anton Blanchardstatic void __exit nvram_cleanup(void) 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds misc_deregister( &nvram_dev ); 5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(nvram_init); 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(nvram_cleanup); 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 578