nvram_64.c revision 0f4ac132365e56802cbe377313491aa84086371c
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
87f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = -ENODEV;
88f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (!ppc_md.nvram_size)
89f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		goto out;
90f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann
91f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = 0;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size = ppc_md.nvram_size();
93f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (*ppos >= size || size < 0)
94f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		goto out;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
96f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	count = min_t(size_t, count, size - *ppos);
97f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	count = min(count, PAGE_SIZE);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
99f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = -ENOMEM;
100f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	tmp = kmalloc(count, GFP_KERNEL);
101f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (!tmp)
102f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		goto out;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
104f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = ppc_md.nvram_read(tmp, count, ppos);
105f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (ret <= 0)
106f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		goto out;
107f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann
108f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (copy_to_user(buf, tmp, ret))
109f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		ret = -EFAULT;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
111f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmannout:
112f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	kfree(tmp);
113f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	return ret;
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t dev_nvram_write(struct file *file, const char __user *buf,
118f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann			  size_t count, loff_t *ppos)
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
120f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ssize_t ret;
121f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	char *tmp = NULL;
122f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ssize_t size;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
124f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = -ENODEV;
125f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (!ppc_md.nvram_size)
126f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		goto out;
127f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann
128f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = 0;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size = ppc_md.nvram_size();
130f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (*ppos >= size || size < 0)
131f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		goto out;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
133f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	count = min_t(size_t, count, size - *ppos);
134f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	count = min(count, PAGE_SIZE);
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
136f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = -ENOMEM;
137f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	tmp = kmalloc(count, GFP_KERNEL);
138f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (!tmp)
139f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		goto out;
140f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann
141f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = -EFAULT;
142f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	if (copy_from_user(tmp, buf, count))
143f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann		goto out;
144f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann
145f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	ret = ppc_md.nvram_write(tmp, count, ppos);
146f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann
147f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmannout:
148f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	kfree(tmp);
149f9ce299fc629d5c899a2e56b00e21f5da05cf590Arnd Bergmann	return ret;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1533b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixnerstatic long dev_nvram_ioctl(struct file *file, unsigned int cmd,
1543b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner			    unsigned long arg)
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch(cmd) {
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_PPC_PMAC
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case OBSOLETE_PMAC_NVRAM_GET_OFFSET:
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n");
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case IOC_NVRAM_GET_OFFSET: {
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int part, offset;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
163e8222502ee6157e2713da9e0792c21f4ad458d50Benjamin Herrenschmidt		if (!machine_is(powermac))
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0)
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (part < pmac_nvram_OF || part > pmac_nvram_NR)
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		offset = pmac_get_partition(part);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (offset < 0)
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return offset;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0)
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* CONFIG_PPC_PMAC */
177af308377e204e25f1f58627d05fe0f483703b514Stephen Rothwell	default:
178af308377e204e25f1f58627d05fe0f483703b514Stephen Rothwell		return -EINVAL;
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1825dfe4c964a0dd7bb3a1d64a4166835a153146207Arjan van de Venconst struct file_operations nvram_fops = {
1833b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner	.owner		= THIS_MODULE,
1843b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner	.llseek		= dev_nvram_llseek,
1853b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner	.read		= dev_nvram_read,
1863b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner	.write		= dev_nvram_write,
1873b03fecd12c4f2a0a3ea33612606320ad23e64feThomas Gleixner	.unlocked_ioctl	= dev_nvram_ioctl,
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice nvram_dev = {
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NVRAM_MINOR,
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"nvram",
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&nvram_fops
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_NVRAM
19832c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic void __init nvram_print_partitions(char * label)
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nvram_partition * tmp_part;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_WARNING "--------%s---------\n", label);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n");
204690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(tmp_part, &nvram_partitions, partition) {
2056024ede9ba84aa1b891c2d6bc98eda07801235e5Jim Keniston		printk(KERN_WARNING "%4d    \t%02x\t%02x\t%d\t%12s\n",
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       tmp_part->index, tmp_part->header.signature,
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       tmp_part->header.checksum, tmp_part->header.length,
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       tmp_part->header.name);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21432c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic int __init nvram_write_header(struct nvram_partition * part)
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	loff_t tmp_index;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tmp_index = part->index;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = ppc_md.nvram_write((char *)&part->header, NVRAM_HEADER_LEN, &tmp_index);
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22632c105c3781d32c55429fbac493602028913390aThomas Gleixnerstatic unsigned char __init nvram_checksum(struct nvram_header *p)
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int c_sum, c_sum2;
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short *sp = (unsigned short *)p->name; /* assume 6 shorts */
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	c_sum = p->signature + p->length + sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5];
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The sum may have spilled into the 3rd byte.  Fold it back. */
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The sum cannot exceed 2 bytes.  Fold it into a checksum */
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	c_sum2 = (c_sum >> 8) + (c_sum << 8);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	c_sum = ((c_sum + c_sum2) >> 8) & 0xff;
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return c_sum;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2400f4ac132365e56802cbe377313491aa84086371cJim Keniston/*
2410f4ac132365e56802cbe377313491aa84086371cJim Keniston * Per the criteria passed via nvram_remove_partition(), should this
2420f4ac132365e56802cbe377313491aa84086371cJim Keniston * partition be removed?  1=remove, 0=keep
2430f4ac132365e56802cbe377313491aa84086371cJim Keniston */
2440f4ac132365e56802cbe377313491aa84086371cJim Kenistonstatic int nvram_can_remove_partition(struct nvram_partition *part,
2450f4ac132365e56802cbe377313491aa84086371cJim Keniston		const char *name, int sig, const char *exceptions[])
2460f4ac132365e56802cbe377313491aa84086371cJim Keniston{
2470f4ac132365e56802cbe377313491aa84086371cJim Keniston	if (part->header.signature != sig)
2480f4ac132365e56802cbe377313491aa84086371cJim Keniston		return 0;
2490f4ac132365e56802cbe377313491aa84086371cJim Keniston	if (name) {
2500f4ac132365e56802cbe377313491aa84086371cJim Keniston		if (strncmp(name, part->header.name, 12))
2510f4ac132365e56802cbe377313491aa84086371cJim Keniston			return 0;
2520f4ac132365e56802cbe377313491aa84086371cJim Keniston	} else if (exceptions) {
2530f4ac132365e56802cbe377313491aa84086371cJim Keniston		const char **except;
2540f4ac132365e56802cbe377313491aa84086371cJim Keniston		for (except = exceptions; *except; except++) {
2550f4ac132365e56802cbe377313491aa84086371cJim Keniston			if (!strncmp(*except, part->header.name, 12))
2560f4ac132365e56802cbe377313491aa84086371cJim Keniston				return 0;
2570f4ac132365e56802cbe377313491aa84086371cJim Keniston		}
2580f4ac132365e56802cbe377313491aa84086371cJim Keniston	}
2590f4ac132365e56802cbe377313491aa84086371cJim Keniston	return 1;
2600f4ac132365e56802cbe377313491aa84086371cJim Keniston}
2610f4ac132365e56802cbe377313491aa84086371cJim Keniston
262fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt/**
263fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * nvram_remove_partition - Remove one or more partitions in nvram
264fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * @name: name of the partition to remove, or NULL for a
265fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt *        signature only match
266fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * @sig: signature of the partition(s) to remove
2670f4ac132365e56802cbe377313491aa84086371cJim Keniston * @exceptions: When removing all partitions with a matching signature,
2680f4ac132365e56802cbe377313491aa84086371cJim Keniston *        leave these alone.
269fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt */
270fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt
2710f4ac132365e56802cbe377313491aa84086371cJim Kenistonint __init nvram_remove_partition(const char *name, int sig,
2720f4ac132365e56802cbe377313491aa84086371cJim Keniston						const char *exceptions[])
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
274fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt	struct nvram_partition *part, *prev, *tmp;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
277690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(part, &nvram_partitions, partition) {
2780f4ac132365e56802cbe377313491aa84086371cJim Keniston		if (!nvram_can_remove_partition(part, name, sig, exceptions))
279fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			continue;
280fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt
281fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		/* Make partition a free partition */
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		part->header.signature = NVRAM_SIG_FREE;
2836024ede9ba84aa1b891c2d6bc98eda07801235e5Jim Keniston		strncpy(part->header.name, "wwwwwwwwwwww", 12);
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		part->header.checksum = nvram_checksum(&part->header);
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = nvram_write_header(part);
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc <= 0) {
287fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return rc;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
290fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt	}
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
292fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt	/* Merge contiguous ones */
293fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt	prev = NULL;
294690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry_safe(part, tmp, &nvram_partitions, partition) {
295fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		if (part->header.signature != NVRAM_SIG_FREE) {
296fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			prev = NULL;
297fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			continue;
298fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		}
299fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		if (prev) {
300fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			prev->header.length += part->header.length;
301fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			prev->header.checksum = nvram_checksum(&part->header);
302fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			rc = nvram_write_header(part);
303fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			if (rc <= 0) {
304fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt				printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
305fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt				return rc;
306fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			}
307fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			list_del(&part->partition);
308fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			kfree(part);
309fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		} else
310fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			prev = part;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3164e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt/**
3174e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * nvram_create_partition - Create a partition in nvram
3184e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @name: name of the partition to create
3194e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @sig: signature of the partition to create
32036673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt * @req_size: size of data to allocate in bytes
3214e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @min_size: minimum acceptable size (0 means req_size)
322e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt *
323e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * Returns a negative error code or a positive nvram index
324e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * of the beginning of the data area of the newly created
325e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * partition. If you provided a min_size smaller than req_size
326e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * you need to query for the actual size yourself after the
327e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * call using nvram_partition_get_size().
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
329edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtloff_t __init nvram_create_partition(const char *name, int sig,
330edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt				     int req_size, int min_size)
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
332a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann	struct nvram_partition *part;
333a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann	struct nvram_partition *new_part;
3340339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton	struct nvram_partition *free_part = NULL;
335cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt	static char nv_init_vals[16];
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	loff_t tmp_index;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	long size = 0;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
3394e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt
34036673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	/* Convert sizes from bytes to blocks */
34136673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	req_size = _ALIGN_UP(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
34236673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	min_size = _ALIGN_UP(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
34336673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt
3444e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	/* If no minimum size specified, make it the same as the
3454e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	 * requested size
3464e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	 */
3474e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	if (min_size == 0)
3484e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt		min_size = req_size;
349e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	if (min_size > req_size)
350e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		return -EINVAL;
3514e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt
35236673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	/* Now add one block to each for the header */
35336673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	req_size += 1;
35436673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	min_size += 1;
35536673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Find a free partition that will give us the maximum needed size
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   If can't find one that will give us the minimum size needed */
358690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(part, &nvram_partitions, partition) {
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (part->header.signature != NVRAM_SIG_FREE)
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3624e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt		if (part->header.length >= req_size) {
3634e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt			size = req_size;
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_part = part;
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3674e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt		if (part->header.length > size &&
3684e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt		    part->header.length >= min_size) {
3694e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt			size = part->header.length;
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_part = part;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3730339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton	if (!size)
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOSPC;
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Create our OS partition */
3770339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton	new_part = kmalloc(sizeof(*new_part), GFP_KERNEL);
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!new_part) {
379e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		pr_err("nvram_create_os_partition: kmalloc failed\n");
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	new_part->index = free_part->index;
3844e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	new_part->header.signature = sig;
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	new_part->header.length = size;
3864e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	strncpy(new_part->header.name, name, 12);
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	new_part->header.checksum = nvram_checksum(&new_part->header);
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = nvram_write_header(new_part);
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc <= 0) {
391e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		pr_err("nvram_create_os_partition: nvram_write_header "
392e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		       "failed (%d)\n", rc);
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return rc;
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
395e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	list_add_tail(&new_part->partition, &free_part->partition);
396e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt
397e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	/* Adjust or remove the partition we stole the space from */
398e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	if (free_part->header.length > size) {
399e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		free_part->index += size * NVRAM_BLOCK_LEN;
400e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		free_part->header.length -= size;
401e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		free_part->header.checksum = nvram_checksum(&free_part->header);
402e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		rc = nvram_write_header(free_part);
403e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		if (rc <= 0) {
404e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt			pr_err("nvram_create_os_partition: nvram_write_header "
405e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt			       "failed (%d)\n", rc);
406e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt			return rc;
407e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		}
408e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	} else {
409e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		list_del(&free_part->partition);
410e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		kfree(free_part);
411e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	}
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
413e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	/* Clear the new partition */
414cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt	for (tmp_index = new_part->index + NVRAM_HEADER_LEN;
415cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt	     tmp_index <  ((size - 1) * NVRAM_BLOCK_LEN);
416cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt	     tmp_index += NVRAM_BLOCK_LEN) {
417cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt		rc = ppc_md.nvram_write(nv_init_vals, NVRAM_BLOCK_LEN, &tmp_index);
418cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt		if (rc <= 0) {
419cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt			pr_err("nvram_create_partition: nvram_write failed (%d)\n", rc);
420cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt			return rc;
421cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt		}
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
424e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	return new_part->index + NVRAM_HEADER_LEN;
425e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt}
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
427e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt/**
428e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * nvram_get_partition_size - Get the data size of an nvram partition
429e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * @data_index: This is the offset of the start of the data of
430e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt *              the partition. The same value that is returned by
431e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt *              nvram_create_partition().
432e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt */
433edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtint nvram_get_partition_size(loff_t data_index)
434e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt{
435e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	struct nvram_partition *part;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
437690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(part, &nvram_partitions, partition) {
438e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		if (part->index + NVRAM_HEADER_LEN == data_index)
439e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt			return (part->header.length - 1) * NVRAM_BLOCK_LEN;
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
441e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	return -1;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
445cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt/**
446cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * nvram_find_partition - Find an nvram partition by signature and name
447cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @name: Name of the partition or NULL for any name
448cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @sig: Signature to test against
449cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @out_size: if non-NULL, returns the size of the data part of the partition
450cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt */
451cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidtloff_t nvram_find_partition(const char *name, int sig, int *out_size)
452cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt{
453cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt	struct nvram_partition *p;
454cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt
455690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(p, &nvram_partitions, partition) {
456cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt		if (p->header.signature == sig &&
457cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt		    (!name || !strncmp(p->header.name, name, 12))) {
458cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt			if (out_size)
459cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt				*out_size = (p->header.length - 1) *
460cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt					NVRAM_BLOCK_LEN;
461cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt			return p->index + NVRAM_HEADER_LEN;
462cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt		}
463cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt	}
464cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt	return 0;
465cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt}
466cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt
467edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtint __init nvram_scan_partitions(void)
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	loff_t cur_index = 0;
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nvram_header phead;
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nvram_partition * tmp_part;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char c_sum;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char * header;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int total_size;
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
477edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt	if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	total_size = ppc_md.nvram_size();
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4815cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day	header = kmalloc(NVRAM_HEADER_LEN, GFP_KERNEL);
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!header) {
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "nvram_scan_partitions: Failed kmalloc\n");
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (cur_index < total_size) {
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = ppc_md.nvram_read(header, NVRAM_HEADER_LEN, &cur_index);
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err != NVRAM_HEADER_LEN) {
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_ERR "nvram_scan_partitions: Error parsing "
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       "nvram partitions\n");
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_index -= NVRAM_HEADER_LEN; /* nvram_read will advance us */
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&phead, header, NVRAM_HEADER_LEN);
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = 0;
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		c_sum = nvram_checksum(&phead);
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (c_sum != phead.checksum) {
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "WARNING: nvram partition checksum"
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       " was %02x, should be %02x!\n",
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       phead.checksum, c_sum);
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "Terminating nvram partition scan\n");
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!phead.length) {
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "WARNING: nvram corruption "
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       "detected: 0-length partition\n");
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp_part = (struct nvram_partition *)
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kmalloc(sizeof(struct nvram_partition), GFP_KERNEL);
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -ENOMEM;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!tmp_part) {
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_ERR "nvram_scan_partitions: kmalloc failed\n");
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN);
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp_part->index = cur_index;
524690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston		list_add_tail(&tmp_part->partition, &nvram_partitions);
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_index += phead.length * NVRAM_BLOCK_LEN;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = 0;
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
530edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt#ifdef DEBUG_NVRAM
531edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt	nvram_print_partitions("NVRAM Partitions");
532edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt#endif
533edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out:
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(header);
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init nvram_init(void)
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
543578914cffc283b907777796420148d582072cbaeBenjamin Herrenschmidt	BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16);
544578914cffc283b907777796420148d582072cbaeBenjamin Herrenschmidt
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return  -ENODEV;
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  	rc = misc_register(&nvram_dev);
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc != 0) {
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "nvram_init: failed to register device\n");
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return rc;
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  	return rc;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid __exit nvram_cleanup(void)
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        misc_deregister( &nvram_dev );
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(nvram_init);
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(nvram_cleanup);
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
565