1 /* 2 * OS info memory interface 3 * 4 * Copyright IBM Corp. 2012 5 * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com> 6 */ 7 8 #define KMSG_COMPONENT "os_info" 9 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 10 11 #include <linux/crash_dump.h> 12 #include <linux/kernel.h> 13 #include <linux/slab.h> 14 #include <asm/checksum.h> 15 #include <asm/lowcore.h> 16 #include <asm/os_info.h> 17 18 /* 19 * OS info structure has to be page aligned 20 */ 21 static struct os_info os_info __page_aligned_data; 22 23 /* 24 * Compute checksum over OS info structure 25 */ 26 u32 os_info_csum(struct os_info *os_info) 27 { 28 int size = sizeof(*os_info) - offsetof(struct os_info, version_major); 29 return (__force u32)csum_partial(&os_info->version_major, size, 0); 30 } 31 32 /* 33 * Add crashkernel info to OS info and update checksum 34 */ 35 void os_info_crashkernel_add(unsigned long base, unsigned long size) 36 { 37 os_info.crashkernel_addr = (u64)(unsigned long)base; 38 os_info.crashkernel_size = (u64)(unsigned long)size; 39 os_info.csum = os_info_csum(&os_info); 40 } 41 42 /* 43 * Add OS info entry and update checksum 44 */ 45 void os_info_entry_add(int nr, void *ptr, u64 size) 46 { 47 os_info.entry[nr].addr = (u64)(unsigned long)ptr; 48 os_info.entry[nr].size = size; 49 os_info.entry[nr].csum = (__force u32)csum_partial(ptr, size, 0); 50 os_info.csum = os_info_csum(&os_info); 51 } 52 53 /* 54 * Initialize OS info struture and set lowcore pointer 55 */ 56 void __init os_info_init(void) 57 { 58 void *ptr = &os_info; 59 60 os_info.version_major = OS_INFO_VERSION_MAJOR; 61 os_info.version_minor = OS_INFO_VERSION_MINOR; 62 os_info.magic = OS_INFO_MAGIC; 63 os_info.csum = os_info_csum(&os_info); 64 mem_assign_absolute(S390_lowcore.os_info, (unsigned long) ptr); 65 } 66 67 #ifdef CONFIG_CRASH_DUMP 68 69 static struct os_info *os_info_old; 70 71 /* 72 * Allocate and copy OS info entry from oldmem 73 */ 74 static void os_info_old_alloc(int nr, int align) 75 { 76 unsigned long addr, size = 0; 77 char *buf, *buf_align, *msg; 78 u32 csum; 79 80 addr = os_info_old->entry[nr].addr; 81 if (!addr) { 82 msg = "not available"; 83 goto fail; 84 } 85 size = os_info_old->entry[nr].size; 86 buf = kmalloc(size + align - 1, GFP_KERNEL); 87 if (!buf) { 88 msg = "alloc failed"; 89 goto fail; 90 } 91 buf_align = PTR_ALIGN(buf, align); 92 if (copy_oldmem_kernel(buf_align, (void *) addr, size)) { 93 msg = "copy failed"; 94 goto fail_free; 95 } 96 csum = (__force u32)csum_partial(buf_align, size, 0); 97 if (csum != os_info_old->entry[nr].csum) { 98 msg = "checksum failed"; 99 goto fail_free; 100 } 101 os_info_old->entry[nr].addr = (u64)(unsigned long)buf_align; 102 msg = "copied"; 103 goto out; 104 fail_free: 105 kfree(buf); 106 fail: 107 os_info_old->entry[nr].addr = 0; 108 out: 109 pr_info("entry %i: %s (addr=0x%lx size=%lu)\n", 110 nr, msg, addr, size); 111 } 112 113 /* 114 * Initialize os info and os info entries from oldmem 115 */ 116 static void os_info_old_init(void) 117 { 118 static int os_info_init; 119 unsigned long addr; 120 121 if (os_info_init) 122 return; 123 if (!OLDMEM_BASE) 124 goto fail; 125 if (copy_oldmem_kernel(&addr, &S390_lowcore.os_info, sizeof(addr))) 126 goto fail; 127 if (addr == 0 || addr % PAGE_SIZE) 128 goto fail; 129 os_info_old = kzalloc(sizeof(*os_info_old), GFP_KERNEL); 130 if (!os_info_old) 131 goto fail; 132 if (copy_oldmem_kernel(os_info_old, (void *) addr, 133 sizeof(*os_info_old))) 134 goto fail_free; 135 if (os_info_old->magic != OS_INFO_MAGIC) 136 goto fail_free; 137 if (os_info_old->csum != os_info_csum(os_info_old)) 138 goto fail_free; 139 if (os_info_old->version_major > OS_INFO_VERSION_MAJOR) 140 goto fail_free; 141 os_info_old_alloc(OS_INFO_VMCOREINFO, 1); 142 os_info_old_alloc(OS_INFO_REIPL_BLOCK, 1); 143 pr_info("crashkernel: addr=0x%lx size=%lu\n", 144 (unsigned long) os_info_old->crashkernel_addr, 145 (unsigned long) os_info_old->crashkernel_size); 146 os_info_init = 1; 147 return; 148 fail_free: 149 kfree(os_info_old); 150 fail: 151 os_info_init = 1; 152 os_info_old = NULL; 153 } 154 155 /* 156 * Return pointer to os infor entry and its size 157 */ 158 void *os_info_old_entry(int nr, unsigned long *size) 159 { 160 os_info_old_init(); 161 162 if (!os_info_old) 163 return NULL; 164 if (!os_info_old->entry[nr].addr) 165 return NULL; 166 *size = (unsigned long) os_info_old->entry[nr].size; 167 return (void *)(unsigned long)os_info_old->entry[nr].addr; 168 } 169 #endif 170