Commit | Line | Data |
---|---|---|
4857d4bb MH |
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> | |
4857d4bb MH |
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); | |
fbe76568 | 63 | mem_assign_absolute(S390_lowcore.os_info, (unsigned long) ptr); |
4857d4bb MH |
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); | |
4857d4bb MH |
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 |