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