diff options
Diffstat (limited to 'kernel/kexec.c')
-rw-r--r-- | kernel/kexec.c | 285 |
1 files changed, 281 insertions, 4 deletions
diff --git a/kernel/kexec.c b/kernel/kexec.c index 25db14b89e82..aa74a1ef2da8 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c | |||
@@ -17,21 +17,30 @@ | |||
17 | #include <linux/highmem.h> | 17 | #include <linux/highmem.h> |
18 | #include <linux/syscalls.h> | 18 | #include <linux/syscalls.h> |
19 | #include <linux/reboot.h> | 19 | #include <linux/reboot.h> |
20 | #include <linux/syscalls.h> | ||
21 | #include <linux/ioport.h> | 20 | #include <linux/ioport.h> |
22 | #include <linux/hardirq.h> | 21 | #include <linux/hardirq.h> |
23 | #include <linux/elf.h> | 22 | #include <linux/elf.h> |
24 | #include <linux/elfcore.h> | 23 | #include <linux/elfcore.h> |
24 | #include <linux/utsrelease.h> | ||
25 | #include <linux/utsname.h> | ||
26 | #include <linux/numa.h> | ||
25 | 27 | ||
26 | #include <asm/page.h> | 28 | #include <asm/page.h> |
27 | #include <asm/uaccess.h> | 29 | #include <asm/uaccess.h> |
28 | #include <asm/io.h> | 30 | #include <asm/io.h> |
29 | #include <asm/system.h> | 31 | #include <asm/system.h> |
30 | #include <asm/semaphore.h> | 32 | #include <asm/semaphore.h> |
33 | #include <asm/sections.h> | ||
31 | 34 | ||
32 | /* Per cpu memory for storing cpu states in case of system crash. */ | 35 | /* Per cpu memory for storing cpu states in case of system crash. */ |
33 | note_buf_t* crash_notes; | 36 | note_buf_t* crash_notes; |
34 | 37 | ||
38 | /* vmcoreinfo stuff */ | ||
39 | unsigned char vmcoreinfo_data[VMCOREINFO_BYTES]; | ||
40 | u32 vmcoreinfo_note[VMCOREINFO_NOTE_SIZE/4]; | ||
41 | size_t vmcoreinfo_size; | ||
42 | size_t vmcoreinfo_max_size = sizeof(vmcoreinfo_data); | ||
43 | |||
35 | /* Location of the reserved area for the crash kernel */ | 44 | /* Location of the reserved area for the crash kernel */ |
36 | struct resource crashk_res = { | 45 | struct resource crashk_res = { |
37 | .name = "Crash kernel", | 46 | .name = "Crash kernel", |
@@ -42,7 +51,7 @@ struct resource crashk_res = { | |||
42 | 51 | ||
43 | int kexec_should_crash(struct task_struct *p) | 52 | int kexec_should_crash(struct task_struct *p) |
44 | { | 53 | { |
45 | if (in_interrupt() || !p->pid || is_init(p) || panic_on_oops) | 54 | if (in_interrupt() || !p->pid || is_global_init(p) || panic_on_oops) |
46 | return 1; | 55 | return 1; |
47 | return 0; | 56 | return 0; |
48 | } | 57 | } |
@@ -776,7 +785,7 @@ static int kimage_load_normal_segment(struct kimage *image, | |||
776 | size_t uchunk, mchunk; | 785 | size_t uchunk, mchunk; |
777 | 786 | ||
778 | page = kimage_alloc_page(image, GFP_HIGHUSER, maddr); | 787 | page = kimage_alloc_page(image, GFP_HIGHUSER, maddr); |
779 | if (page == 0) { | 788 | if (!page) { |
780 | result = -ENOMEM; | 789 | result = -ENOMEM; |
781 | goto out; | 790 | goto out; |
782 | } | 791 | } |
@@ -835,7 +844,7 @@ static int kimage_load_crash_segment(struct kimage *image, | |||
835 | size_t uchunk, mchunk; | 844 | size_t uchunk, mchunk; |
836 | 845 | ||
837 | page = pfn_to_page(maddr >> PAGE_SHIFT); | 846 | page = pfn_to_page(maddr >> PAGE_SHIFT); |
838 | if (page == 0) { | 847 | if (!page) { |
839 | result = -ENOMEM; | 848 | result = -ENOMEM; |
840 | goto out; | 849 | goto out; |
841 | } | 850 | } |
@@ -1061,6 +1070,7 @@ void crash_kexec(struct pt_regs *regs) | |||
1061 | if (kexec_crash_image) { | 1070 | if (kexec_crash_image) { |
1062 | struct pt_regs fixed_regs; | 1071 | struct pt_regs fixed_regs; |
1063 | crash_setup_regs(&fixed_regs, regs); | 1072 | crash_setup_regs(&fixed_regs, regs); |
1073 | crash_save_vmcoreinfo(); | ||
1064 | machine_crash_shutdown(&fixed_regs); | 1074 | machine_crash_shutdown(&fixed_regs); |
1065 | machine_kexec(kexec_crash_image); | 1075 | machine_kexec(kexec_crash_image); |
1066 | } | 1076 | } |
@@ -1135,3 +1145,270 @@ static int __init crash_notes_memory_init(void) | |||
1135 | return 0; | 1145 | return 0; |
1136 | } | 1146 | } |
1137 | module_init(crash_notes_memory_init) | 1147 | module_init(crash_notes_memory_init) |
1148 | |||
1149 | |||
1150 | /* | ||
1151 | * parsing the "crashkernel" commandline | ||
1152 | * | ||
1153 | * this code is intended to be called from architecture specific code | ||
1154 | */ | ||
1155 | |||
1156 | |||
1157 | /* | ||
1158 | * This function parses command lines in the format | ||
1159 | * | ||
1160 | * crashkernel=ramsize-range:size[,...][@offset] | ||
1161 | * | ||
1162 | * The function returns 0 on success and -EINVAL on failure. | ||
1163 | */ | ||
1164 | static int __init parse_crashkernel_mem(char *cmdline, | ||
1165 | unsigned long long system_ram, | ||
1166 | unsigned long long *crash_size, | ||
1167 | unsigned long long *crash_base) | ||
1168 | { | ||
1169 | char *cur = cmdline, *tmp; | ||
1170 | |||
1171 | /* for each entry of the comma-separated list */ | ||
1172 | do { | ||
1173 | unsigned long long start, end = ULLONG_MAX, size; | ||
1174 | |||
1175 | /* get the start of the range */ | ||
1176 | start = memparse(cur, &tmp); | ||
1177 | if (cur == tmp) { | ||
1178 | pr_warning("crashkernel: Memory value expected\n"); | ||
1179 | return -EINVAL; | ||
1180 | } | ||
1181 | cur = tmp; | ||
1182 | if (*cur != '-') { | ||
1183 | pr_warning("crashkernel: '-' expected\n"); | ||
1184 | return -EINVAL; | ||
1185 | } | ||
1186 | cur++; | ||
1187 | |||
1188 | /* if no ':' is here, than we read the end */ | ||
1189 | if (*cur != ':') { | ||
1190 | end = memparse(cur, &tmp); | ||
1191 | if (cur == tmp) { | ||
1192 | pr_warning("crashkernel: Memory " | ||
1193 | "value expected\n"); | ||
1194 | return -EINVAL; | ||
1195 | } | ||
1196 | cur = tmp; | ||
1197 | if (end <= start) { | ||
1198 | pr_warning("crashkernel: end <= start\n"); | ||
1199 | return -EINVAL; | ||
1200 | } | ||
1201 | } | ||
1202 | |||
1203 | if (*cur != ':') { | ||
1204 | pr_warning("crashkernel: ':' expected\n"); | ||
1205 | return -EINVAL; | ||
1206 | } | ||
1207 | cur++; | ||
1208 | |||
1209 | size = memparse(cur, &tmp); | ||
1210 | if (cur == tmp) { | ||
1211 | pr_warning("Memory value expected\n"); | ||
1212 | return -EINVAL; | ||
1213 | } | ||
1214 | cur = tmp; | ||
1215 | if (size >= system_ram) { | ||
1216 | pr_warning("crashkernel: invalid size\n"); | ||
1217 | return -EINVAL; | ||
1218 | } | ||
1219 | |||
1220 | /* match ? */ | ||
1221 | if (system_ram >= start && system_ram <= end) { | ||
1222 | *crash_size = size; | ||
1223 | break; | ||
1224 | } | ||
1225 | } while (*cur++ == ','); | ||
1226 | |||
1227 | if (*crash_size > 0) { | ||
1228 | while (*cur != ' ' && *cur != '@') | ||
1229 | cur++; | ||
1230 | if (*cur == '@') { | ||
1231 | cur++; | ||
1232 | *crash_base = memparse(cur, &tmp); | ||
1233 | if (cur == tmp) { | ||
1234 | pr_warning("Memory value expected " | ||
1235 | "after '@'\n"); | ||
1236 | return -EINVAL; | ||
1237 | } | ||
1238 | } | ||
1239 | } | ||
1240 | |||
1241 | return 0; | ||
1242 | } | ||
1243 | |||
1244 | /* | ||
1245 | * That function parses "simple" (old) crashkernel command lines like | ||
1246 | * | ||
1247 | * crashkernel=size[@offset] | ||
1248 | * | ||
1249 | * It returns 0 on success and -EINVAL on failure. | ||
1250 | */ | ||
1251 | static int __init parse_crashkernel_simple(char *cmdline, | ||
1252 | unsigned long long *crash_size, | ||
1253 | unsigned long long *crash_base) | ||
1254 | { | ||
1255 | char *cur = cmdline; | ||
1256 | |||
1257 | *crash_size = memparse(cmdline, &cur); | ||
1258 | if (cmdline == cur) { | ||
1259 | pr_warning("crashkernel: memory value expected\n"); | ||
1260 | return -EINVAL; | ||
1261 | } | ||
1262 | |||
1263 | if (*cur == '@') | ||
1264 | *crash_base = memparse(cur+1, &cur); | ||
1265 | |||
1266 | return 0; | ||
1267 | } | ||
1268 | |||
1269 | /* | ||
1270 | * That function is the entry point for command line parsing and should be | ||
1271 | * called from the arch-specific code. | ||
1272 | */ | ||
1273 | int __init parse_crashkernel(char *cmdline, | ||
1274 | unsigned long long system_ram, | ||
1275 | unsigned long long *crash_size, | ||
1276 | unsigned long long *crash_base) | ||
1277 | { | ||
1278 | char *p = cmdline, *ck_cmdline = NULL; | ||
1279 | char *first_colon, *first_space; | ||
1280 | |||
1281 | BUG_ON(!crash_size || !crash_base); | ||
1282 | *crash_size = 0; | ||
1283 | *crash_base = 0; | ||
1284 | |||
1285 | /* find crashkernel and use the last one if there are more */ | ||
1286 | p = strstr(p, "crashkernel="); | ||
1287 | while (p) { | ||
1288 | ck_cmdline = p; | ||
1289 | p = strstr(p+1, "crashkernel="); | ||
1290 | } | ||
1291 | |||
1292 | if (!ck_cmdline) | ||
1293 | return -EINVAL; | ||
1294 | |||
1295 | ck_cmdline += 12; /* strlen("crashkernel=") */ | ||
1296 | |||
1297 | /* | ||
1298 | * if the commandline contains a ':', then that's the extended | ||
1299 | * syntax -- if not, it must be the classic syntax | ||
1300 | */ | ||
1301 | first_colon = strchr(ck_cmdline, ':'); | ||
1302 | first_space = strchr(ck_cmdline, ' '); | ||
1303 | if (first_colon && (!first_space || first_colon < first_space)) | ||
1304 | return parse_crashkernel_mem(ck_cmdline, system_ram, | ||
1305 | crash_size, crash_base); | ||
1306 | else | ||
1307 | return parse_crashkernel_simple(ck_cmdline, crash_size, | ||
1308 | crash_base); | ||
1309 | |||
1310 | return 0; | ||
1311 | } | ||
1312 | |||
1313 | |||
1314 | |||
1315 | void crash_save_vmcoreinfo(void) | ||
1316 | { | ||
1317 | u32 *buf; | ||
1318 | |||
1319 | if (!vmcoreinfo_size) | ||
1320 | return; | ||
1321 | |||
1322 | vmcoreinfo_append_str("CRASHTIME=%ld", get_seconds()); | ||
1323 | |||
1324 | buf = (u32 *)vmcoreinfo_note; | ||
1325 | |||
1326 | buf = append_elf_note(buf, VMCOREINFO_NOTE_NAME, 0, vmcoreinfo_data, | ||
1327 | vmcoreinfo_size); | ||
1328 | |||
1329 | final_note(buf); | ||
1330 | } | ||
1331 | |||
1332 | void vmcoreinfo_append_str(const char *fmt, ...) | ||
1333 | { | ||
1334 | va_list args; | ||
1335 | char buf[0x50]; | ||
1336 | int r; | ||
1337 | |||
1338 | va_start(args, fmt); | ||
1339 | r = vsnprintf(buf, sizeof(buf), fmt, args); | ||
1340 | va_end(args); | ||
1341 | |||
1342 | if (r + vmcoreinfo_size > vmcoreinfo_max_size) | ||
1343 | r = vmcoreinfo_max_size - vmcoreinfo_size; | ||
1344 | |||
1345 | memcpy(&vmcoreinfo_data[vmcoreinfo_size], buf, r); | ||
1346 | |||
1347 | vmcoreinfo_size += r; | ||
1348 | } | ||
1349 | |||
1350 | /* | ||
1351 | * provide an empty default implementation here -- architecture | ||
1352 | * code may override this | ||
1353 | */ | ||
1354 | void __attribute__ ((weak)) arch_crash_save_vmcoreinfo(void) | ||
1355 | {} | ||
1356 | |||
1357 | unsigned long __attribute__ ((weak)) paddr_vmcoreinfo_note(void) | ||
1358 | { | ||
1359 | return __pa((unsigned long)(char *)&vmcoreinfo_note); | ||
1360 | } | ||
1361 | |||
1362 | static int __init crash_save_vmcoreinfo_init(void) | ||
1363 | { | ||
1364 | vmcoreinfo_append_str("OSRELEASE=%s\n", init_uts_ns.name.release); | ||
1365 | vmcoreinfo_append_str("PAGESIZE=%ld\n", PAGE_SIZE); | ||
1366 | |||
1367 | VMCOREINFO_SYMBOL(init_uts_ns); | ||
1368 | VMCOREINFO_SYMBOL(node_online_map); | ||
1369 | VMCOREINFO_SYMBOL(swapper_pg_dir); | ||
1370 | VMCOREINFO_SYMBOL(_stext); | ||
1371 | |||
1372 | #ifndef CONFIG_NEED_MULTIPLE_NODES | ||
1373 | VMCOREINFO_SYMBOL(mem_map); | ||
1374 | VMCOREINFO_SYMBOL(contig_page_data); | ||
1375 | #endif | ||
1376 | #ifdef CONFIG_SPARSEMEM | ||
1377 | VMCOREINFO_SYMBOL(mem_section); | ||
1378 | VMCOREINFO_LENGTH(mem_section, NR_SECTION_ROOTS); | ||
1379 | VMCOREINFO_SIZE(mem_section); | ||
1380 | VMCOREINFO_OFFSET(mem_section, section_mem_map); | ||
1381 | #endif | ||
1382 | VMCOREINFO_SIZE(page); | ||
1383 | VMCOREINFO_SIZE(pglist_data); | ||
1384 | VMCOREINFO_SIZE(zone); | ||
1385 | VMCOREINFO_SIZE(free_area); | ||
1386 | VMCOREINFO_SIZE(list_head); | ||
1387 | VMCOREINFO_TYPEDEF_SIZE(nodemask_t); | ||
1388 | VMCOREINFO_OFFSET(page, flags); | ||
1389 | VMCOREINFO_OFFSET(page, _count); | ||
1390 | VMCOREINFO_OFFSET(page, mapping); | ||
1391 | VMCOREINFO_OFFSET(page, lru); | ||
1392 | VMCOREINFO_OFFSET(pglist_data, node_zones); | ||
1393 | VMCOREINFO_OFFSET(pglist_data, nr_zones); | ||
1394 | #ifdef CONFIG_FLAT_NODE_MEM_MAP | ||
1395 | VMCOREINFO_OFFSET(pglist_data, node_mem_map); | ||
1396 | #endif | ||
1397 | VMCOREINFO_OFFSET(pglist_data, node_start_pfn); | ||
1398 | VMCOREINFO_OFFSET(pglist_data, node_spanned_pages); | ||
1399 | VMCOREINFO_OFFSET(pglist_data, node_id); | ||
1400 | VMCOREINFO_OFFSET(zone, free_area); | ||
1401 | VMCOREINFO_OFFSET(zone, vm_stat); | ||
1402 | VMCOREINFO_OFFSET(zone, spanned_pages); | ||
1403 | VMCOREINFO_OFFSET(free_area, free_list); | ||
1404 | VMCOREINFO_OFFSET(list_head, next); | ||
1405 | VMCOREINFO_OFFSET(list_head, prev); | ||
1406 | VMCOREINFO_LENGTH(zone.free_area, MAX_ORDER); | ||
1407 | VMCOREINFO_NUMBER(NR_FREE_PAGES); | ||
1408 | |||
1409 | arch_crash_save_vmcoreinfo(); | ||
1410 | |||
1411 | return 0; | ||
1412 | } | ||
1413 | |||
1414 | module_init(crash_save_vmcoreinfo_init) | ||