Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f4ac132 authored by Jim Keniston's avatar Jim Keniston Committed by Benjamin Herrenschmidt
Browse files

powerpc/nvram: Generalize code for OS partitions in NVRAM



Adapt the functions used to create and write to the RTAS-log partition
to work with any OS-type partition.

Signed-off-by: default avatarJim Keniston <jkenisto@us.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 6dd22700
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -51,7 +51,8 @@ static inline int mmio_nvram_init(void)
extern int __init nvram_scan_partitions(void);
extern loff_t nvram_create_partition(const char *name, int sig,
				     int req_size, int min_size);
extern int nvram_remove_partition(const char *name, int sig);
extern int nvram_remove_partition(const char *name, int sig,
					const char *exceptions[]);
extern int nvram_get_partition_size(loff_t data_index);
extern loff_t nvram_find_partition(const char *name, int sig, int *out_size);

+27 −4
Original line number Diff line number Diff line
@@ -237,22 +237,45 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
	return c_sum;
}

/*
 * Per the criteria passed via nvram_remove_partition(), should this
 * partition be removed?  1=remove, 0=keep
 */
static int nvram_can_remove_partition(struct nvram_partition *part,
		const char *name, int sig, const char *exceptions[])
{
	if (part->header.signature != sig)
		return 0;
	if (name) {
		if (strncmp(name, part->header.name, 12))
			return 0;
	} else if (exceptions) {
		const char **except;
		for (except = exceptions; *except; except++) {
			if (!strncmp(*except, part->header.name, 12))
				return 0;
		}
	}
	return 1;
}

/**
 * nvram_remove_partition - Remove one or more partitions in nvram
 * @name: name of the partition to remove, or NULL for a
 *        signature only match
 * @sig: signature of the partition(s) to remove
 * @exceptions: When removing all partitions with a matching signature,
 *        leave these alone.
 */

int __init nvram_remove_partition(const char *name, int sig)
int __init nvram_remove_partition(const char *name, int sig,
						const char *exceptions[])
{
	struct nvram_partition *part, *prev, *tmp;
	int rc;

	list_for_each_entry(part, &nvram_partitions, partition) {
		if (part->header.signature != sig)
			continue;
		if (name && strncmp(name, part->header.name, 12))
		if (!nvram_can_remove_partition(part, name, sig, exceptions))
			continue;

		/* Make partition a free partition */
+84 −59
Original line number Diff line number Diff line
@@ -30,17 +30,30 @@ static int nvram_fetch, nvram_store;
static char nvram_buf[NVRW_CNT];	/* assume this is in the first 4GB */
static DEFINE_SPINLOCK(nvram_lock);

static long nvram_error_log_index = -1;
static long nvram_error_log_size = 0;

struct err_log_info {
	int error_type;
	unsigned int seq_num;
};
#define NVRAM_MAX_REQ		2079
#define NVRAM_MIN_REQ		1055

#define NVRAM_LOG_PART_NAME	"ibm,rtas-log"
struct nvram_os_partition {
	const char *name;
	int req_size;	/* desired size, in bytes */
	int min_size;	/* minimum acceptable size (0 means req_size) */
	long size;	/* size of data portion of partition */
	long index;	/* offset of data portion of partition */
};

static struct nvram_os_partition rtas_log_partition = {
	.name = "ibm,rtas-log",
	.req_size = 2079,
	.min_size = 1055,
	.index = -1
};

static const char *pseries_nvram_os_partitions[] = {
	"ibm,rtas-log",
	NULL
};

static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
{
@@ -134,7 +147,7 @@ static ssize_t pSeries_nvram_get_size(void)
}


/* nvram_write_error_log
/* nvram_write_os_partition, nvram_write_error_log
 *
 * We need to buffer the error logs into nvram to ensure that we have
 * the failure information to decode.  If we have a severe error there
@@ -156,48 +169,55 @@ static ssize_t pSeries_nvram_get_size(void)
 * The 'data' section would look like (in bytes):
 * +--------------+------------+-----------------------------------+
 * | event_logged | sequence # | error log                         |
 * |0            3|4          7|8            nvram_error_log_size-1|
 * |0            3|4          7|8                  error_log_size-1|
 * +--------------+------------+-----------------------------------+
 *
 * event_logged: 0 if event has not been logged to syslog, 1 if it has
 * sequence #: The unique sequence # for each event. (until it wraps)
 * error log: The error log from event_scan
 */
int nvram_write_error_log(char * buff, int length,
                          unsigned int err_type, unsigned int error_log_cnt)
int nvram_write_os_partition(struct nvram_os_partition *part, char * buff,
		int length, unsigned int err_type, unsigned int error_log_cnt)
{
	int rc;
	loff_t tmp_index;
	struct err_log_info info;
	
	if (nvram_error_log_index == -1) {
	if (part->index == -1) {
		return -ESPIPE;
	}

	if (length > nvram_error_log_size) {
		length = nvram_error_log_size;
	if (length > part->size) {
		length = part->size;
	}

	info.error_type = err_type;
	info.seq_num = error_log_cnt;

	tmp_index = nvram_error_log_index;
	tmp_index = part->index;

	rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
	if (rc <= 0) {
		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
		pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc);
		return rc;
	}

	rc = ppc_md.nvram_write(buff, length, &tmp_index);
	if (rc <= 0) {
		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
		pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc);
		return rc;
	}
	
	return 0;
}

int nvram_write_error_log(char * buff, int length,
                          unsigned int err_type, unsigned int error_log_cnt)
{
	return nvram_write_os_partition(&rtas_log_partition, buff, length,
						err_type, error_log_cnt);
}

/* nvram_read_error_log
 *
 * Reads nvram for error log for at most 'length'
@@ -209,13 +229,13 @@ int nvram_read_error_log(char * buff, int length,
	loff_t tmp_index;
	struct err_log_info info;
	
	if (nvram_error_log_index == -1)
	if (rtas_log_partition.index == -1)
		return -1;

	if (length > nvram_error_log_size)
		length = nvram_error_log_size;
	if (length > rtas_log_partition.size)
		length = rtas_log_partition.size;

	tmp_index = nvram_error_log_index;
	tmp_index = rtas_log_partition.index;

	rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
	if (rc <= 0) {
@@ -244,10 +264,10 @@ int nvram_clear_error_log(void)
	int clear_word = ERR_FLAG_ALREADY_LOGGED;
	int rc;

	if (nvram_error_log_index == -1)
	if (rtas_log_partition.index == -1)
		return -1;

	tmp_index = nvram_error_log_index;
	tmp_index = rtas_log_partition.index;
	
	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
	if (rc <= 0) {
@@ -258,23 +278,25 @@ int nvram_clear_error_log(void)
	return 0;
}

/* pseries_nvram_init_log_partition
/* pseries_nvram_init_os_partition
 *
 * This will setup the partition we need for buffering the
 * error logs and cleanup partitions if needed.
 * This sets up a partition with an "OS" signature.
 *
 * The general strategy is the following:
 * 1.) If there is log partition large enough then use it.
 * 2.) If there is none large enough, search
 * for a free partition that is large enough.
 * 3.) If there is not a free partition large enough remove 
 * _all_ OS partitions and consolidate the space.
 * 4.) Will first try getting a chunk that will satisfy the maximum
 * error log size (NVRAM_MAX_REQ).
 * 5.) If the max chunk cannot be allocated then try finding a chunk
 * that will satisfy the minum needed (NVRAM_MIN_REQ).
 * 1.) If a partition with the indicated name already exists...
 *	- If it's large enough, use it.
 *	- Otherwise, recycle it and keep going.
 * 2.) Search for a free partition that is large enough.
 * 3.) If there's not a free partition large enough, recycle any obsolete
 * OS partitions and try again.
 * 4.) Will first try getting a chunk that will satisfy the requested size.
 * 5.) If a chunk of the requested size cannot be allocated, then try finding
 * a chunk that will satisfy the minum needed.
 *
 * Returns 0 on success, else -1.
 */
static int __init pseries_nvram_init_log_partition(void)
static int __init pseries_nvram_init_os_partition(struct nvram_os_partition
									*part)
{
	loff_t p;
	int size;
@@ -282,47 +304,50 @@ static int __init pseries_nvram_init_log_partition(void)
	/* Scan nvram for partitions */
	nvram_scan_partitions();

	/* Lookg for ours */
	p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size);
	/* Look for ours */
	p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);

	/* Found one but too small, remove it */
	if (p && size < NVRAM_MIN_REQ) {
		pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition"
			",removing it...");
		nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS);
	if (p && size < part->min_size) {
		pr_info("nvram: Found too small %s partition,"
					" removing it...\n", part->name);
		nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL);
		p = 0;
	}

	/* Create one if we didn't find */
	if (!p) {
		p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS,
					   NVRAM_MAX_REQ, NVRAM_MIN_REQ);
		/* No room for it, try to get rid of any OS partition
		 * and try again
		 */
		p = nvram_create_partition(part->name, NVRAM_SIG_OS,
					part->req_size, part->min_size);
		if (p == -ENOSPC) {
			pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME
				" partition, deleting all OS partitions...");
			nvram_remove_partition(NULL, NVRAM_SIG_OS);
			p = nvram_create_partition(NVRAM_LOG_PART_NAME,
						   NVRAM_SIG_OS, NVRAM_MAX_REQ,
						   NVRAM_MIN_REQ);
			pr_info("nvram: No room to create %s partition, "
				"deleting any obsolete OS partitions...\n",
				part->name);
			nvram_remove_partition(NULL, NVRAM_SIG_OS,
						pseries_nvram_os_partitions);
			p = nvram_create_partition(part->name, NVRAM_SIG_OS,
					part->req_size, part->min_size);
		}
	}

	if (p <= 0) {
		pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME
		       " partition, err %d\n", (int)p);
		return 0;
		pr_err("nvram: Failed to find or create %s"
		       " partition, err %d\n", part->name, (int)p);
		return -1;
	}

	nvram_error_log_index = p;
	nvram_error_log_size = nvram_get_partition_size(p) -
		sizeof(struct err_log_info);
	part->index = p;
	part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info);
	
	return 0;
}
machine_arch_initcall(pseries, pseries_nvram_init_log_partition);

static int __init pseries_nvram_init_log_partitions(void)
{
	(void) pseries_nvram_init_os_partition(&rtas_log_partition);
	return 0;
}
machine_arch_initcall(pseries, pseries_nvram_init_log_partitions);

int __init pSeries_nvram_init(void)
{