diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2017-06-28 06:57:47 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2017-06-28 06:57:47 -0400 |
commit | 9e293b5a7062981016ace93160c56a980fcb73b8 (patch) | |
tree | 69f557de9c1a1c2deaa28bdc42f8228a33247116 | |
parent | 795c9a5106119f45d2501c4fb01051178904753f (diff) | |
parent | da72ca4d4090a8ab0e6b0a23682ef42d39d7ae00 (diff) |
Merge tag 'nmiforkvm' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into features
Pull kvm patches from Christian Borntraeger:
"s390,kvm: provide plumbing for machines checks when running guests"
This provides the basic plumbing for handling machine checks when
running guests
-rw-r--r-- | arch/s390/include/asm/kvm_host.h | 17 | ||||
-rw-r--r-- | arch/s390/include/asm/nmi.h | 7 | ||||
-rw-r--r-- | arch/s390/include/asm/processor.h | 2 | ||||
-rw-r--r-- | arch/s390/kernel/asm-offsets.c | 3 | ||||
-rw-r--r-- | arch/s390/kernel/entry.S | 13 | ||||
-rw-r--r-- | arch/s390/kernel/nmi.c | 84 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 1 |
7 files changed, 115 insertions, 12 deletions
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 426614a882a9..c6e1d5fa1ad1 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h | |||
@@ -107,6 +107,20 @@ struct esca_block { | |||
107 | struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS]; | 107 | struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS]; |
108 | } __packed; | 108 | } __packed; |
109 | 109 | ||
110 | /* | ||
111 | * This struct is used to store some machine check info from lowcore | ||
112 | * for machine checks that happen while the guest is running. | ||
113 | * This info in host's lowcore might be overwritten by a second machine | ||
114 | * check from host when host is in the machine check's high-level handling. | ||
115 | * The size is 24 bytes. | ||
116 | */ | ||
117 | struct mcck_volatile_info { | ||
118 | __u64 mcic; | ||
119 | __u64 failing_storage_address; | ||
120 | __u32 ext_damage_code; | ||
121 | __u32 reserved; | ||
122 | }; | ||
123 | |||
110 | #define CPUSTAT_STOPPED 0x80000000 | 124 | #define CPUSTAT_STOPPED 0x80000000 |
111 | #define CPUSTAT_WAIT 0x10000000 | 125 | #define CPUSTAT_WAIT 0x10000000 |
112 | #define CPUSTAT_ECALL_PEND 0x08000000 | 126 | #define CPUSTAT_ECALL_PEND 0x08000000 |
@@ -264,7 +278,8 @@ struct kvm_s390_itdb { | |||
264 | 278 | ||
265 | struct sie_page { | 279 | struct sie_page { |
266 | struct kvm_s390_sie_block sie_block; | 280 | struct kvm_s390_sie_block sie_block; |
267 | __u8 reserved200[1024]; /* 0x0200 */ | 281 | struct mcck_volatile_info mcck_info; /* 0x0200 */ |
282 | __u8 reserved218[1000]; /* 0x0218 */ | ||
268 | struct kvm_s390_itdb itdb; /* 0x0600 */ | 283 | struct kvm_s390_itdb itdb; /* 0x0600 */ |
269 | __u8 reserved700[2304]; /* 0x0700 */ | 284 | __u8 reserved700[2304]; /* 0x0700 */ |
270 | } __packed; | 285 | } __packed; |
diff --git a/arch/s390/include/asm/nmi.h b/arch/s390/include/asm/nmi.h index e3e8895f5d3e..13623b9991d4 100644 --- a/arch/s390/include/asm/nmi.h +++ b/arch/s390/include/asm/nmi.h | |||
@@ -14,7 +14,14 @@ | |||
14 | #include <linux/const.h> | 14 | #include <linux/const.h> |
15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
16 | 16 | ||
17 | #define MCIC_SUBCLASS_MASK (1ULL<<63 | 1ULL<<62 | 1ULL<<61 | \ | ||
18 | 1ULL<<59 | 1ULL<<58 | 1ULL<<56 | \ | ||
19 | 1ULL<<55 | 1ULL<<54 | 1ULL<<53 | \ | ||
20 | 1ULL<<52 | 1ULL<<47 | 1ULL<<46 | \ | ||
21 | 1ULL<<45 | 1ULL<<44) | ||
17 | #define MCCK_CODE_SYSTEM_DAMAGE _BITUL(63) | 22 | #define MCCK_CODE_SYSTEM_DAMAGE _BITUL(63) |
23 | #define MCCK_CODE_EXT_DAMAGE _BITUL(63 - 5) | ||
24 | #define MCCK_CODE_CP _BITUL(63 - 9) | ||
18 | #define MCCK_CODE_CPU_TIMER_VALID _BITUL(63 - 46) | 25 | #define MCCK_CODE_CPU_TIMER_VALID _BITUL(63 - 46) |
19 | #define MCCK_CODE_PSW_MWP_VALID _BITUL(63 - 20) | 26 | #define MCCK_CODE_PSW_MWP_VALID _BITUL(63 - 20) |
20 | #define MCCK_CODE_PSW_IA_VALID _BITUL(63 - 23) | 27 | #define MCCK_CODE_PSW_IA_VALID _BITUL(63 - 23) |
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index f57c017a5c03..72c7b88f8d2c 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h | |||
@@ -20,6 +20,7 @@ | |||
20 | #define CIF_FPU 4 /* restore FPU registers */ | 20 | #define CIF_FPU 4 /* restore FPU registers */ |
21 | #define CIF_IGNORE_IRQ 5 /* ignore interrupt (for udelay) */ | 21 | #define CIF_IGNORE_IRQ 5 /* ignore interrupt (for udelay) */ |
22 | #define CIF_ENABLED_WAIT 6 /* in enabled wait state */ | 22 | #define CIF_ENABLED_WAIT 6 /* in enabled wait state */ |
23 | #define CIF_MCCK_GUEST 7 /* machine check happening in guest */ | ||
23 | 24 | ||
24 | #define _CIF_MCCK_PENDING _BITUL(CIF_MCCK_PENDING) | 25 | #define _CIF_MCCK_PENDING _BITUL(CIF_MCCK_PENDING) |
25 | #define _CIF_ASCE_PRIMARY _BITUL(CIF_ASCE_PRIMARY) | 26 | #define _CIF_ASCE_PRIMARY _BITUL(CIF_ASCE_PRIMARY) |
@@ -28,6 +29,7 @@ | |||
28 | #define _CIF_FPU _BITUL(CIF_FPU) | 29 | #define _CIF_FPU _BITUL(CIF_FPU) |
29 | #define _CIF_IGNORE_IRQ _BITUL(CIF_IGNORE_IRQ) | 30 | #define _CIF_IGNORE_IRQ _BITUL(CIF_IGNORE_IRQ) |
30 | #define _CIF_ENABLED_WAIT _BITUL(CIF_ENABLED_WAIT) | 31 | #define _CIF_ENABLED_WAIT _BITUL(CIF_ENABLED_WAIT) |
32 | #define _CIF_MCCK_GUEST _BITUL(CIF_MCCK_GUEST) | ||
31 | 33 | ||
32 | #ifndef __ASSEMBLY__ | 34 | #ifndef __ASSEMBLY__ |
33 | 35 | ||
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 6bb29633e1f1..b65c414b6c0e 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c | |||
@@ -58,6 +58,9 @@ int main(void) | |||
58 | OFFSET(__SF_BACKCHAIN, stack_frame, back_chain); | 58 | OFFSET(__SF_BACKCHAIN, stack_frame, back_chain); |
59 | OFFSET(__SF_GPRS, stack_frame, gprs); | 59 | OFFSET(__SF_GPRS, stack_frame, gprs); |
60 | OFFSET(__SF_EMPTY, stack_frame, empty1); | 60 | OFFSET(__SF_EMPTY, stack_frame, empty1); |
61 | OFFSET(__SF_SIE_CONTROL, stack_frame, empty1[0]); | ||
62 | OFFSET(__SF_SIE_SAVEAREA, stack_frame, empty1[1]); | ||
63 | OFFSET(__SF_SIE_REASON, stack_frame, empty1[2]); | ||
61 | BLANK(); | 64 | BLANK(); |
62 | /* timeval/timezone offsets for use by vdso */ | 65 | /* timeval/timezone offsets for use by vdso */ |
63 | OFFSET(__VDSO_UPD_COUNT, vdso_data, tb_update_count); | 66 | OFFSET(__VDSO_UPD_COUNT, vdso_data, tb_update_count); |
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index e83aff630bcf..21900e1cee9c 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S | |||
@@ -225,6 +225,7 @@ ENTRY(sie64a) | |||
225 | jnz .Lsie_skip | 225 | jnz .Lsie_skip |
226 | TSTMSK __LC_CPU_FLAGS,_CIF_FPU | 226 | TSTMSK __LC_CPU_FLAGS,_CIF_FPU |
227 | jo .Lsie_skip # exit if fp/vx regs changed | 227 | jo .Lsie_skip # exit if fp/vx regs changed |
228 | .Lsie_entry: | ||
228 | sie 0(%r14) | 229 | sie 0(%r14) |
229 | .Lsie_skip: | 230 | .Lsie_skip: |
230 | ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE | 231 | ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE |
@@ -1122,7 +1123,13 @@ cleanup_critical: | |||
1122 | .quad .Lsie_done | 1123 | .quad .Lsie_done |
1123 | 1124 | ||
1124 | .Lcleanup_sie: | 1125 | .Lcleanup_sie: |
1125 | lg %r9,__SF_EMPTY(%r15) # get control block pointer | 1126 | cghi %r11,__LC_SAVE_AREA_ASYNC #Is this in normal interrupt? |
1127 | je 1f | ||
1128 | slg %r9,BASED(.Lsie_crit_mcck_start) | ||
1129 | clg %r9,BASED(.Lsie_crit_mcck_length) | ||
1130 | jh 1f | ||
1131 | oi __LC_CPU_FLAGS+7, _CIF_MCCK_GUEST | ||
1132 | 1: lg %r9,__SF_EMPTY(%r15) # get control block pointer | ||
1126 | ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE | 1133 | ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE |
1127 | lctlg %c1,%c1,__LC_USER_ASCE # load primary asce | 1134 | lctlg %c1,%c1,__LC_USER_ASCE # load primary asce |
1128 | larl %r9,sie_exit # skip forward to sie_exit | 1135 | larl %r9,sie_exit # skip forward to sie_exit |
@@ -1307,6 +1314,10 @@ cleanup_critical: | |||
1307 | .quad .Lsie_gmap | 1314 | .quad .Lsie_gmap |
1308 | .Lsie_critical_length: | 1315 | .Lsie_critical_length: |
1309 | .quad .Lsie_done - .Lsie_gmap | 1316 | .quad .Lsie_done - .Lsie_gmap |
1317 | .Lsie_crit_mcck_start: | ||
1318 | .quad .Lsie_entry | ||
1319 | .Lsie_crit_mcck_length: | ||
1320 | .quad .Lsie_skip - .Lsie_entry | ||
1310 | #endif | 1321 | #endif |
1311 | 1322 | ||
1312 | .section .rodata, "a" | 1323 | .section .rodata, "a" |
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 985589523970..31d03a84126c 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c | |||
@@ -25,6 +25,8 @@ | |||
25 | #include <asm/crw.h> | 25 | #include <asm/crw.h> |
26 | #include <asm/switch_to.h> | 26 | #include <asm/switch_to.h> |
27 | #include <asm/ctl_reg.h> | 27 | #include <asm/ctl_reg.h> |
28 | #include <asm/asm-offsets.h> | ||
29 | #include <linux/kvm_host.h> | ||
28 | 30 | ||
29 | struct mcck_struct { | 31 | struct mcck_struct { |
30 | unsigned int kill_task : 1; | 32 | unsigned int kill_task : 1; |
@@ -274,12 +276,39 @@ static int notrace s390_validate_registers(union mci mci, int umode) | |||
274 | return kill_task; | 276 | return kill_task; |
275 | } | 277 | } |
276 | 278 | ||
279 | /* | ||
280 | * Backup the guest's machine check info to its description block | ||
281 | */ | ||
282 | static void notrace s390_backup_mcck_info(struct pt_regs *regs) | ||
283 | { | ||
284 | struct mcck_volatile_info *mcck_backup; | ||
285 | struct sie_page *sie_page; | ||
286 | |||
287 | /* r14 contains the sie block, which was set in sie64a */ | ||
288 | struct kvm_s390_sie_block *sie_block = | ||
289 | (struct kvm_s390_sie_block *) regs->gprs[14]; | ||
290 | |||
291 | if (sie_block == NULL) | ||
292 | /* Something's seriously wrong, stop system. */ | ||
293 | s390_handle_damage(); | ||
294 | |||
295 | sie_page = container_of(sie_block, struct sie_page, sie_block); | ||
296 | mcck_backup = &sie_page->mcck_info; | ||
297 | mcck_backup->mcic = S390_lowcore.mcck_interruption_code & | ||
298 | ~(MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE); | ||
299 | mcck_backup->ext_damage_code = S390_lowcore.external_damage_code; | ||
300 | mcck_backup->failing_storage_address | ||
301 | = S390_lowcore.failing_storage_address; | ||
302 | } | ||
303 | |||
277 | #define MAX_IPD_COUNT 29 | 304 | #define MAX_IPD_COUNT 29 |
278 | #define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */ | 305 | #define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */ |
279 | 306 | ||
280 | #define ED_STP_ISLAND 6 /* External damage STP island check */ | 307 | #define ED_STP_ISLAND 6 /* External damage STP island check */ |
281 | #define ED_STP_SYNC 7 /* External damage STP sync check */ | 308 | #define ED_STP_SYNC 7 /* External damage STP sync check */ |
282 | 309 | ||
310 | #define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE) | ||
311 | |||
283 | /* | 312 | /* |
284 | * machine check handler. | 313 | * machine check handler. |
285 | */ | 314 | */ |
@@ -291,6 +320,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) | |||
291 | struct mcck_struct *mcck; | 320 | struct mcck_struct *mcck; |
292 | unsigned long long tmp; | 321 | unsigned long long tmp; |
293 | union mci mci; | 322 | union mci mci; |
323 | unsigned long mcck_dam_code; | ||
294 | 324 | ||
295 | nmi_enter(); | 325 | nmi_enter(); |
296 | inc_irq_stat(NMI_NMI); | 326 | inc_irq_stat(NMI_NMI); |
@@ -301,7 +331,13 @@ void notrace s390_do_machine_check(struct pt_regs *regs) | |||
301 | /* System damage -> stopping machine */ | 331 | /* System damage -> stopping machine */ |
302 | s390_handle_damage(); | 332 | s390_handle_damage(); |
303 | } | 333 | } |
304 | if (mci.pd) { | 334 | |
335 | /* | ||
336 | * Reinject the instruction processing damages' machine checks | ||
337 | * including Delayed Access Exception into the guest | ||
338 | * instead of damaging the host if they happen in the guest. | ||
339 | */ | ||
340 | if (mci.pd && !test_cpu_flag(CIF_MCCK_GUEST)) { | ||
305 | if (mci.b) { | 341 | if (mci.b) { |
306 | /* Processing backup -> verify if we can survive this */ | 342 | /* Processing backup -> verify if we can survive this */ |
307 | u64 z_mcic, o_mcic, t_mcic; | 343 | u64 z_mcic, o_mcic, t_mcic; |
@@ -345,6 +381,14 @@ void notrace s390_do_machine_check(struct pt_regs *regs) | |||
345 | mcck->mcck_code = mci.val; | 381 | mcck->mcck_code = mci.val; |
346 | set_cpu_flag(CIF_MCCK_PENDING); | 382 | set_cpu_flag(CIF_MCCK_PENDING); |
347 | } | 383 | } |
384 | |||
385 | /* | ||
386 | * Backup the machine check's info if it happens when the guest | ||
387 | * is running. | ||
388 | */ | ||
389 | if (test_cpu_flag(CIF_MCCK_GUEST)) | ||
390 | s390_backup_mcck_info(regs); | ||
391 | |||
348 | if (mci.cd) { | 392 | if (mci.cd) { |
349 | /* Timing facility damage */ | 393 | /* Timing facility damage */ |
350 | s390_handle_damage(); | 394 | s390_handle_damage(); |
@@ -358,15 +402,22 @@ void notrace s390_do_machine_check(struct pt_regs *regs) | |||
358 | if (mcck->stp_queue) | 402 | if (mcck->stp_queue) |
359 | set_cpu_flag(CIF_MCCK_PENDING); | 403 | set_cpu_flag(CIF_MCCK_PENDING); |
360 | } | 404 | } |
361 | if (mci.se) | 405 | |
362 | /* Storage error uncorrected */ | 406 | /* |
363 | s390_handle_damage(); | 407 | * Reinject storage related machine checks into the guest if they |
364 | if (mci.ke) | 408 | * happen when the guest is running. |
365 | /* Storage key-error uncorrected */ | 409 | */ |
366 | s390_handle_damage(); | 410 | if (!test_cpu_flag(CIF_MCCK_GUEST)) { |
367 | if (mci.ds && mci.fa) | 411 | if (mci.se) |
368 | /* Storage degradation */ | 412 | /* Storage error uncorrected */ |
369 | s390_handle_damage(); | 413 | s390_handle_damage(); |
414 | if (mci.ke) | ||
415 | /* Storage key-error uncorrected */ | ||
416 | s390_handle_damage(); | ||
417 | if (mci.ds && mci.fa) | ||
418 | /* Storage degradation */ | ||
419 | s390_handle_damage(); | ||
420 | } | ||
370 | if (mci.cp) { | 421 | if (mci.cp) { |
371 | /* Channel report word pending */ | 422 | /* Channel report word pending */ |
372 | mcck->channel_report = 1; | 423 | mcck->channel_report = 1; |
@@ -377,6 +428,19 @@ void notrace s390_do_machine_check(struct pt_regs *regs) | |||
377 | mcck->warning = 1; | 428 | mcck->warning = 1; |
378 | set_cpu_flag(CIF_MCCK_PENDING); | 429 | set_cpu_flag(CIF_MCCK_PENDING); |
379 | } | 430 | } |
431 | |||
432 | /* | ||
433 | * If there are only Channel Report Pending and External Damage | ||
434 | * machine checks, they will not be reinjected into the guest | ||
435 | * because they refer to host conditions only. | ||
436 | */ | ||
437 | mcck_dam_code = (mci.val & MCIC_SUBCLASS_MASK); | ||
438 | if (test_cpu_flag(CIF_MCCK_GUEST) && | ||
439 | (mcck_dam_code & MCCK_CODE_NO_GUEST) != mcck_dam_code) { | ||
440 | /* Set exit reason code for host's later handling */ | ||
441 | *((long *)(regs->gprs[15] + __SF_SIE_REASON)) = -EINTR; | ||
442 | } | ||
443 | clear_cpu_flag(CIF_MCCK_GUEST); | ||
380 | nmi_exit(); | 444 | nmi_exit(); |
381 | } | 445 | } |
382 | 446 | ||
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 689ac48361c6..0457e03199c5 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -2069,6 +2069,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, | |||
2069 | if (!vcpu) | 2069 | if (!vcpu) |
2070 | goto out; | 2070 | goto out; |
2071 | 2071 | ||
2072 | BUILD_BUG_ON(sizeof(struct sie_page) != 4096); | ||
2072 | sie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL); | 2073 | sie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL); |
2073 | if (!sie_page) | 2074 | if (!sie_page) |
2074 | goto out_free_cpu; | 2075 | goto out_free_cpu; |