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