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