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