os_info.c 3.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
/*
 * OS info memory interface
 *
 * Copyright IBM Corp. 2012
 * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
 */

#define KMSG_COMPONENT "os_info"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

#include <linux/crash_dump.h>
#include <linux/kernel.h>
#include <asm/checksum.h>
#include <asm/lowcore.h>
#include <asm/os_info.h>

/*
 * OS info structure has to be page aligned
 */
static struct os_info os_info __page_aligned_data;

/*
 * Compute checksum over OS info structure
 */
u32 os_info_csum(struct os_info *os_info)
{
	int size = sizeof(*os_info) - offsetof(struct os_info, version_major);
	return csum_partial(&os_info->version_major, size, 0);
}

/*
 * Add crashkernel info to OS info and update checksum
 */
void os_info_crashkernel_add(unsigned long base, unsigned long size)
{
	os_info.crashkernel_addr = (u64)(unsigned long)base;
	os_info.crashkernel_size = (u64)(unsigned long)size;
	os_info.csum = os_info_csum(&os_info);
}

/*
 * Add OS info entry and update checksum
 */
void os_info_entry_add(int nr, void *ptr, u64 size)
{
	os_info.entry[nr].addr = (u64)(unsigned long)ptr;
	os_info.entry[nr].size = size;
	os_info.entry[nr].csum = csum_partial(ptr, size, 0);
	os_info.csum = os_info_csum(&os_info);
}

/*
 * Initialize OS info struture and set lowcore pointer
 */
void __init os_info_init(void)
{
	void *ptr = &os_info;

	os_info.version_major = OS_INFO_VERSION_MAJOR;
	os_info.version_minor = OS_INFO_VERSION_MINOR;
	os_info.magic = OS_INFO_MAGIC;
	os_info.csum = os_info_csum(&os_info);
63
	memcpy_absolute(&S390_lowcore.os_info, &ptr, sizeof(ptr));
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
}

#ifdef CONFIG_CRASH_DUMP

static struct os_info *os_info_old;

/*
 * Allocate and copy OS info entry from oldmem
 */
static void os_info_old_alloc(int nr, int align)
{
	unsigned long addr, size = 0;
	char *buf, *buf_align, *msg;
	u32 csum;

	addr = os_info_old->entry[nr].addr;
	if (!addr) {
		msg = "not available";
		goto fail;
	}
	size = os_info_old->entry[nr].size;
	buf = kmalloc(size + align - 1, GFP_KERNEL);
	if (!buf) {
		msg = "alloc failed";
		goto fail;
	}
	buf_align = PTR_ALIGN(buf, align);
	if (copy_from_oldmem(buf_align, (void *) addr, size)) {
		msg = "copy failed";
		goto fail_free;
	}
	csum = csum_partial(buf_align, size, 0);
	if (csum != os_info_old->entry[nr].csum) {
		msg = "checksum failed";
		goto fail_free;
	}
	os_info_old->entry[nr].addr = (u64)(unsigned long)buf_align;
	msg = "copied";
	goto out;
fail_free:
	kfree(buf);
fail:
	os_info_old->entry[nr].addr = 0;
out:
	pr_info("entry %i: %s (addr=0x%lx size=%lu)\n",
		nr, msg, addr, size);
}

/*
 * Initialize os info and os info entries from oldmem
 */
static void os_info_old_init(void)
{
	static int os_info_init;
	unsigned long addr;

	if (os_info_init)
		return;
	if (!OLDMEM_BASE)
		goto fail;
	if (copy_from_oldmem(&addr, &S390_lowcore.os_info, sizeof(addr)))
		goto fail;
	if (addr == 0 || addr % PAGE_SIZE)
		goto fail;
	os_info_old = kzalloc(sizeof(*os_info_old), GFP_KERNEL);
	if (!os_info_old)
		goto fail;
	if (copy_from_oldmem(os_info_old, (void *) addr, sizeof(*os_info_old)))
		goto fail_free;
	if (os_info_old->magic != OS_INFO_MAGIC)
		goto fail_free;
	if (os_info_old->csum != os_info_csum(os_info_old))
		goto fail_free;
	if (os_info_old->version_major > OS_INFO_VERSION_MAJOR)
		goto fail_free;
	os_info_old_alloc(OS_INFO_VMCOREINFO, 1);
	os_info_old_alloc(OS_INFO_REIPL_BLOCK, 1);
	pr_info("crashkernel: addr=0x%lx size=%lu\n",
		(unsigned long) os_info_old->crashkernel_addr,
		(unsigned long) os_info_old->crashkernel_size);
	os_info_init = 1;
	return;
fail_free:
	kfree(os_info_old);
fail:
	os_info_init = 1;
	os_info_old = NULL;
}

/*
 * Return pointer to os infor entry and its size
 */
void *os_info_old_entry(int nr, unsigned long *size)
{
	os_info_old_init();

	if (!os_info_old)
		return NULL;
	if (!os_info_old->entry[nr].addr)
		return NULL;
	*size = (unsigned long) os_info_old->entry[nr].size;
	return (void *)(unsigned long)os_info_old->entry[nr].addr;
}
#endif