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/abs_lowcore.h> 17 #include <asm/os_info.h> 18 #include <asm/physmem_info.h> 19 #include <asm/maccess.h> 20 #include <asm/asm-offsets.h> 21 #include <asm/ipl.h> 22 23 /* 24 * OS info structure has to be page aligned 25 */ 26 static struct os_info os_info __page_aligned_data; 27 28 /* 29 * Compute checksum over OS info structure 30 */ 31 u32 os_info_csum(struct os_info *os_info) 32 { 33 int size = sizeof(*os_info) - offsetof(struct os_info, version_major); 34 return (__force u32)cksm(&os_info->version_major, size, 0); 35 } 36 37 /* 38 * Add crashkernel info to OS info and update checksum 39 */ 40 void os_info_crashkernel_add(unsigned long base, unsigned long size) 41 { 42 os_info.crashkernel_addr = (u64)(unsigned long)base; 43 os_info.crashkernel_size = (u64)(unsigned long)size; 44 os_info.csum = os_info_csum(&os_info); 45 } 46 47 /* 48 * Add OS info data entry and update checksum 49 */ 50 void os_info_entry_add_data(int nr, void *ptr, u64 size) 51 { 52 os_info.entry[nr].addr = __pa(ptr); 53 os_info.entry[nr].size = size; 54 os_info.entry[nr].csum = (__force u32)cksm(ptr, size, 0); 55 os_info.csum = os_info_csum(&os_info); 56 } 57 58 /* 59 * Add OS info value entry and update checksum 60 */ 61 void os_info_entry_add_val(int nr, u64 value) 62 { 63 os_info.entry[nr].val = value; 64 os_info.entry[nr].size = 0; 65 os_info.entry[nr].csum = 0; 66 os_info.csum = os_info_csum(&os_info); 67 } 68 69 /* 70 * Initialize OS info structure and set lowcore pointer 71 */ 72 void __init os_info_init(void) 73 { 74 struct lowcore *abs_lc; 75 76 BUILD_BUG_ON(sizeof(struct os_info) != PAGE_SIZE); 77 os_info.version_major = OS_INFO_VERSION_MAJOR; 78 os_info.version_minor = OS_INFO_VERSION_MINOR; 79 os_info.magic = OS_INFO_MAGIC; 80 os_info_entry_add_val(OS_INFO_IDENTITY_BASE, __identity_base); 81 os_info_entry_add_val(OS_INFO_KASLR_OFFSET, kaslr_offset()); 82 os_info_entry_add_val(OS_INFO_KASLR_OFF_PHYS, __kaslr_offset_phys); 83 os_info_entry_add_val(OS_INFO_VMEMMAP, (unsigned long)vmemmap); 84 os_info_entry_add_val(OS_INFO_AMODE31_START, AMODE31_START); 85 os_info_entry_add_val(OS_INFO_AMODE31_END, AMODE31_END); 86 os_info_entry_add_val(OS_INFO_IMAGE_START, (unsigned long)_stext); 87 os_info_entry_add_val(OS_INFO_IMAGE_END, (unsigned long)_end); 88 os_info_entry_add_val(OS_INFO_IMAGE_PHYS, __pa_symbol(_stext)); 89 os_info.csum = os_info_csum(&os_info); 90 abs_lc = get_abs_lowcore(); 91 abs_lc->os_info = __pa(&os_info); 92 put_abs_lowcore(abs_lc); 93 } 94 95 #ifdef CONFIG_CRASH_DUMP 96 97 static struct os_info *os_info_old; 98 99 /* 100 * Allocate and copy OS info entry from oldmem 101 */ 102 static void os_info_old_alloc(int nr, int align) 103 { 104 unsigned long addr, size = 0; 105 char *buf, *buf_align, *msg; 106 u32 csum; 107 108 addr = os_info_old->entry[nr].addr; 109 if (!addr) { 110 msg = "not available"; 111 goto fail; 112 } 113 size = os_info_old->entry[nr].size; 114 buf = kmalloc(size + align - 1, GFP_KERNEL); 115 if (!buf) { 116 msg = "alloc failed"; 117 goto fail; 118 } 119 buf_align = PTR_ALIGN(buf, align); 120 if (copy_oldmem_kernel(buf_align, addr, size)) { 121 msg = "copy failed"; 122 goto fail_free; 123 } 124 csum = (__force u32)cksm(buf_align, size, 0); 125 if (csum != os_info_old->entry[nr].csum) { 126 msg = "checksum failed"; 127 goto fail_free; 128 } 129 os_info_old->entry[nr].addr = (u64)(unsigned long)buf_align; 130 msg = "copied"; 131 goto out; 132 fail_free: 133 kfree(buf); 134 fail: 135 os_info_old->entry[nr].addr = 0; 136 out: 137 pr_info("entry %i: %s (addr=0x%lx size=%lu)\n", 138 nr, msg, addr, size); 139 } 140 141 /* 142 * Initialize os info and os info entries from oldmem 143 */ 144 static void os_info_old_init(void) 145 { 146 static int os_info_init; 147 unsigned long addr; 148 149 if (os_info_init) 150 return; 151 if (!oldmem_data.start && !is_ipl_type_dump()) 152 goto fail; 153 if (copy_oldmem_kernel(&addr, __LC_OS_INFO, sizeof(addr))) 154 goto fail; 155 if (addr == 0 || addr % PAGE_SIZE) 156 goto fail; 157 os_info_old = kzalloc(sizeof(*os_info_old), GFP_KERNEL); 158 if (!os_info_old) 159 goto fail; 160 if (copy_oldmem_kernel(os_info_old, addr, sizeof(*os_info_old))) 161 goto fail_free; 162 if (os_info_old->magic != OS_INFO_MAGIC) 163 goto fail_free; 164 if (os_info_old->csum != os_info_csum(os_info_old)) 165 goto fail_free; 166 if (os_info_old->version_major > OS_INFO_VERSION_MAJOR) 167 goto fail_free; 168 os_info_old_alloc(OS_INFO_VMCOREINFO, 1); 169 os_info_old_alloc(OS_INFO_REIPL_BLOCK, 1); 170 pr_info("crashkernel: addr=0x%lx size=%lu\n", 171 (unsigned long) os_info_old->crashkernel_addr, 172 (unsigned long) os_info_old->crashkernel_size); 173 os_info_init = 1; 174 return; 175 fail_free: 176 kfree(os_info_old); 177 fail: 178 os_info_init = 1; 179 os_info_old = NULL; 180 } 181 182 /* 183 * Return pointer to os info entry and its size 184 */ 185 void *os_info_old_entry(int nr, unsigned long *size) 186 { 187 os_info_old_init(); 188 189 if (!os_info_old) 190 return NULL; 191 if (!os_info_old->entry[nr].addr) 192 return NULL; 193 *size = (unsigned long) os_info_old->entry[nr].size; 194 return (void *)(unsigned long)os_info_old->entry[nr].addr; 195 } 196 #endif 197