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