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