nvram_64.c revision 6024ede9ba84aa1b891c2d6bc98eda07801235e5
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
240fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt/**
241fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * nvram_remove_partition - Remove one or more partitions in nvram
242fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * @name: name of the partition to remove, or NULL for a
243fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt *        signature only match
244fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt * @sig: signature of the partition(s) to remove
245fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt */
246fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt
247edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtint __init nvram_remove_partition(const char *name, int sig)
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
249fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt	struct nvram_partition *part, *prev, *tmp;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
252690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(part, &nvram_partitions, partition) {
253fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		if (part->header.signature != sig)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
255fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		if (name && strncmp(name, part->header.name, 12))
256fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			continue;
257fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt
258fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		/* Make partition a free partition */
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		part->header.signature = NVRAM_SIG_FREE;
2606024ede9ba84aa1b891c2d6bc98eda07801235e5Jim Keniston		strncpy(part->header.name, "wwwwwwwwwwww", 12);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		part->header.checksum = nvram_checksum(&part->header);
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = nvram_write_header(part);
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc <= 0) {
264fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return rc;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
267fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt	}
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
269fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt	/* Merge contiguous ones */
270fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt	prev = NULL;
271690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry_safe(part, tmp, &nvram_partitions, partition) {
272fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		if (part->header.signature != NVRAM_SIG_FREE) {
273fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			prev = NULL;
274fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			continue;
275fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		}
276fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		if (prev) {
277fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			prev->header.length += part->header.length;
278fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			prev->header.checksum = nvram_checksum(&part->header);
279fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			rc = nvram_write_header(part);
280fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			if (rc <= 0) {
281fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt				printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
282fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt				return rc;
283fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			}
284fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			list_del(&part->partition);
285fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			kfree(part);
286fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt		} else
287fa2b4e54d41f3c9f1bee6a7d63ecd4f0ab161e89Benjamin Herrenschmidt			prev = part;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2934e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt/**
2944e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * nvram_create_partition - Create a partition in nvram
2954e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @name: name of the partition to create
2964e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @sig: signature of the partition to create
29736673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt * @req_size: size of data to allocate in bytes
2984e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt * @min_size: minimum acceptable size (0 means req_size)
299e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt *
300e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * Returns a negative error code or a positive nvram index
301e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * of the beginning of the data area of the newly created
302e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * partition. If you provided a min_size smaller than req_size
303e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * you need to query for the actual size yourself after the
304e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * call using nvram_partition_get_size().
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
306edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtloff_t __init nvram_create_partition(const char *name, int sig,
307edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt				     int req_size, int min_size)
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
309a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann	struct nvram_partition *part;
310a341ad97245d01c923995cfe7deacd0c8aee6e16Arnd Bergmann	struct nvram_partition *new_part;
3110339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton	struct nvram_partition *free_part = NULL;
312cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt	static char nv_init_vals[16];
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	loff_t tmp_index;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	long size = 0;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
3164e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt
31736673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	/* Convert sizes from bytes to blocks */
31836673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	req_size = _ALIGN_UP(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
31936673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	min_size = _ALIGN_UP(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
32036673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt
3214e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	/* If no minimum size specified, make it the same as the
3224e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	 * requested size
3234e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	 */
3244e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	if (min_size == 0)
3254e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt		min_size = req_size;
326e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	if (min_size > req_size)
327e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		return -EINVAL;
3284e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt
32936673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	/* Now add one block to each for the header */
33036673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	req_size += 1;
33136673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt	min_size += 1;
33236673307aee535f951f4eede81049c6962bc4ba9Benjamin Herrenschmidt
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Find a free partition that will give us the maximum needed size
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   If can't find one that will give us the minimum size needed */
335690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(part, &nvram_partitions, partition) {
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (part->header.signature != NVRAM_SIG_FREE)
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3394e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt		if (part->header.length >= req_size) {
3404e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt			size = req_size;
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_part = part;
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3444e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt		if (part->header.length > size &&
3454e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt		    part->header.length >= min_size) {
3464e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt			size = part->header.length;
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_part = part;
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3500339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton	if (!size)
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOSPC;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Create our OS partition */
3540339ad77c4a06fa8529db17c91f790058e18b65bAndrew Morton	new_part = kmalloc(sizeof(*new_part), GFP_KERNEL);
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!new_part) {
356e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		pr_err("nvram_create_os_partition: kmalloc failed\n");
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	new_part->index = free_part->index;
3614e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	new_part->header.signature = sig;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	new_part->header.length = size;
3634e7c77a385efac81d6677a4a761b1b66cd2cb59eBenjamin Herrenschmidt	strncpy(new_part->header.name, name, 12);
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	new_part->header.checksum = nvram_checksum(&new_part->header);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = nvram_write_header(new_part);
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc <= 0) {
368e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		pr_err("nvram_create_os_partition: nvram_write_header "
369e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		       "failed (%d)\n", rc);
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return rc;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
372e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	list_add_tail(&new_part->partition, &free_part->partition);
373e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt
374e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	/* Adjust or remove the partition we stole the space from */
375e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	if (free_part->header.length > size) {
376e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		free_part->index += size * NVRAM_BLOCK_LEN;
377e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		free_part->header.length -= size;
378e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		free_part->header.checksum = nvram_checksum(&free_part->header);
379e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		rc = nvram_write_header(free_part);
380e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		if (rc <= 0) {
381e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt			pr_err("nvram_create_os_partition: nvram_write_header "
382e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt			       "failed (%d)\n", rc);
383e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt			return rc;
384e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		}
385e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	} else {
386e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		list_del(&free_part->partition);
387e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		kfree(free_part);
388e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	}
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
390e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	/* Clear the new partition */
391cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt	for (tmp_index = new_part->index + NVRAM_HEADER_LEN;
392cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt	     tmp_index <  ((size - 1) * NVRAM_BLOCK_LEN);
393cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt	     tmp_index += NVRAM_BLOCK_LEN) {
394cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt		rc = ppc_md.nvram_write(nv_init_vals, NVRAM_BLOCK_LEN, &tmp_index);
395cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt		if (rc <= 0) {
396cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt			pr_err("nvram_create_partition: nvram_write failed (%d)\n", rc);
397cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt			return rc;
398cef0d5ad62ec6e0c8456b8f58e898aa3219311a5Benjamin Herrenschmidt		}
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	return new_part->index + NVRAM_HEADER_LEN;
402e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt}
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
404e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt/**
405e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * nvram_get_partition_size - Get the data size of an nvram partition
406e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt * @data_index: This is the offset of the start of the data of
407e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt *              the partition. The same value that is returned by
408e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt *              nvram_create_partition().
409e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt */
410edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtint nvram_get_partition_size(loff_t data_index)
411e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt{
412e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	struct nvram_partition *part;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
414690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(part, &nvram_partitions, partition) {
415e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt		if (part->index + NVRAM_HEADER_LEN == data_index)
416e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt			return (part->header.length - 1) * NVRAM_BLOCK_LEN;
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
418e49e2e87235518c21b1f5228809209831e6169e7Benjamin Herrenschmidt	return -1;
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
422cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt/**
423cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * nvram_find_partition - Find an nvram partition by signature and name
424cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @name: Name of the partition or NULL for any name
425cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @sig: Signature to test against
426cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt * @out_size: if non-NULL, returns the size of the data part of the partition
427cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt */
428cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidtloff_t nvram_find_partition(const char *name, int sig, int *out_size)
429cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt{
430cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt	struct nvram_partition *p;
431cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt
432690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston	list_for_each_entry(p, &nvram_partitions, partition) {
433cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt		if (p->header.signature == sig &&
434cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt		    (!name || !strncmp(p->header.name, name, 12))) {
435cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt			if (out_size)
436cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt				*out_size = (p->header.length - 1) *
437cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt					NVRAM_BLOCK_LEN;
438cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt			return p->index + NVRAM_HEADER_LEN;
439cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt		}
440cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt	}
441cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt	return 0;
442cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt}
443cf5cbf9f8085eb45316d6e3c888a77cc50696701Benjamin Herrenschmidt
444edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidtint __init nvram_scan_partitions(void)
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	loff_t cur_index = 0;
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nvram_header phead;
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nvram_partition * tmp_part;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char c_sum;
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char * header;
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int total_size;
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
454edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt	if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	total_size = ppc_md.nvram_size();
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4585cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day	header = kmalloc(NVRAM_HEADER_LEN, GFP_KERNEL);
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!header) {
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "nvram_scan_partitions: Failed kmalloc\n");
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (cur_index < total_size) {
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = ppc_md.nvram_read(header, NVRAM_HEADER_LEN, &cur_index);
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (err != NVRAM_HEADER_LEN) {
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_ERR "nvram_scan_partitions: Error parsing "
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       "nvram partitions\n");
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_index -= NVRAM_HEADER_LEN; /* nvram_read will advance us */
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&phead, header, NVRAM_HEADER_LEN);
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = 0;
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		c_sum = nvram_checksum(&phead);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (c_sum != phead.checksum) {
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "WARNING: nvram partition checksum"
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       " was %02x, should be %02x!\n",
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       phead.checksum, c_sum);
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "Terminating nvram partition scan\n");
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!phead.length) {
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "WARNING: nvram corruption "
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       "detected: 0-length partition\n");
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp_part = (struct nvram_partition *)
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kmalloc(sizeof(struct nvram_partition), GFP_KERNEL);
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -ENOMEM;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!tmp_part) {
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_ERR "nvram_scan_partitions: kmalloc failed\n");
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN);
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp_part->index = cur_index;
501690d1a9bd14bd861328ca66473a223f60cf1ad31Jim Keniston		list_add_tail(&tmp_part->partition, &nvram_partitions);
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_index += phead.length * NVRAM_BLOCK_LEN;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = 0;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
507edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt#ifdef DEBUG_NVRAM
508edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt	nvram_print_partitions("NVRAM Partitions");
509edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt#endif
510edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5Benjamin Herrenschmidt
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out:
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(header);
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init nvram_init(void)
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
520578914cffc283b907777796420148d582072cbaeBenjamin Herrenschmidt	BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16);
521578914cffc283b907777796420148d582072cbaeBenjamin Herrenschmidt
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return  -ENODEV;
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  	rc = misc_register(&nvram_dev);
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc != 0) {
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "nvram_init: failed to register device\n");
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return rc;
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  	return rc;
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid __exit nvram_cleanup(void)
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        misc_deregister( &nvram_dev );
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(nvram_init);
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(nvram_cleanup);
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
542