Commit 9e293b5a authored by Martin Schwidefsky's avatar Martin Schwidefsky

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
parents 795c9a51 da72ca4d
...@@ -107,6 +107,20 @@ struct esca_block { ...@@ -107,6 +107,20 @@ struct esca_block {
struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS]; struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS];
} __packed; } __packed;
/*
* This struct is used to store some machine check info from lowcore
* for machine checks that happen while the guest is running.
* This info in host's lowcore might be overwritten by a second machine
* check from host when host is in the machine check's high-level handling.
* The size is 24 bytes.
*/
struct mcck_volatile_info {
__u64 mcic;
__u64 failing_storage_address;
__u32 ext_damage_code;
__u32 reserved;
};
#define CPUSTAT_STOPPED 0x80000000 #define CPUSTAT_STOPPED 0x80000000
#define CPUSTAT_WAIT 0x10000000 #define CPUSTAT_WAIT 0x10000000
#define CPUSTAT_ECALL_PEND 0x08000000 #define CPUSTAT_ECALL_PEND 0x08000000
...@@ -264,7 +278,8 @@ struct kvm_s390_itdb { ...@@ -264,7 +278,8 @@ struct kvm_s390_itdb {
struct sie_page { struct sie_page {
struct kvm_s390_sie_block sie_block; struct kvm_s390_sie_block sie_block;
__u8 reserved200[1024]; /* 0x0200 */ struct mcck_volatile_info mcck_info; /* 0x0200 */
__u8 reserved218[1000]; /* 0x0218 */
struct kvm_s390_itdb itdb; /* 0x0600 */ struct kvm_s390_itdb itdb; /* 0x0600 */
__u8 reserved700[2304]; /* 0x0700 */ __u8 reserved700[2304]; /* 0x0700 */
} __packed; } __packed;
......
...@@ -14,7 +14,14 @@ ...@@ -14,7 +14,14 @@
#include <linux/const.h> #include <linux/const.h>
#include <linux/types.h> #include <linux/types.h>
#define MCIC_SUBCLASS_MASK (1ULL<<63 | 1ULL<<62 | 1ULL<<61 | \
1ULL<<59 | 1ULL<<58 | 1ULL<<56 | \
1ULL<<55 | 1ULL<<54 | 1ULL<<53 | \
1ULL<<52 | 1ULL<<47 | 1ULL<<46 | \
1ULL<<45 | 1ULL<<44)
#define MCCK_CODE_SYSTEM_DAMAGE _BITUL(63) #define MCCK_CODE_SYSTEM_DAMAGE _BITUL(63)
#define MCCK_CODE_EXT_DAMAGE _BITUL(63 - 5)
#define MCCK_CODE_CP _BITUL(63 - 9)
#define MCCK_CODE_CPU_TIMER_VALID _BITUL(63 - 46) #define MCCK_CODE_CPU_TIMER_VALID _BITUL(63 - 46)
#define MCCK_CODE_PSW_MWP_VALID _BITUL(63 - 20) #define MCCK_CODE_PSW_MWP_VALID _BITUL(63 - 20)
#define MCCK_CODE_PSW_IA_VALID _BITUL(63 - 23) #define MCCK_CODE_PSW_IA_VALID _BITUL(63 - 23)
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#define CIF_FPU 4 /* restore FPU registers */ #define CIF_FPU 4 /* restore FPU registers */
#define CIF_IGNORE_IRQ 5 /* ignore interrupt (for udelay) */ #define CIF_IGNORE_IRQ 5 /* ignore interrupt (for udelay) */
#define CIF_ENABLED_WAIT 6 /* in enabled wait state */ #define CIF_ENABLED_WAIT 6 /* in enabled wait state */
#define CIF_MCCK_GUEST 7 /* machine check happening in guest */
#define _CIF_MCCK_PENDING _BITUL(CIF_MCCK_PENDING) #define _CIF_MCCK_PENDING _BITUL(CIF_MCCK_PENDING)
#define _CIF_ASCE_PRIMARY _BITUL(CIF_ASCE_PRIMARY) #define _CIF_ASCE_PRIMARY _BITUL(CIF_ASCE_PRIMARY)
...@@ -28,6 +29,7 @@ ...@@ -28,6 +29,7 @@
#define _CIF_FPU _BITUL(CIF_FPU) #define _CIF_FPU _BITUL(CIF_FPU)
#define _CIF_IGNORE_IRQ _BITUL(CIF_IGNORE_IRQ) #define _CIF_IGNORE_IRQ _BITUL(CIF_IGNORE_IRQ)
#define _CIF_ENABLED_WAIT _BITUL(CIF_ENABLED_WAIT) #define _CIF_ENABLED_WAIT _BITUL(CIF_ENABLED_WAIT)
#define _CIF_MCCK_GUEST _BITUL(CIF_MCCK_GUEST)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
......
...@@ -58,6 +58,9 @@ int main(void) ...@@ -58,6 +58,9 @@ int main(void)
OFFSET(__SF_BACKCHAIN, stack_frame, back_chain); OFFSET(__SF_BACKCHAIN, stack_frame, back_chain);
OFFSET(__SF_GPRS, stack_frame, gprs); OFFSET(__SF_GPRS, stack_frame, gprs);
OFFSET(__SF_EMPTY, stack_frame, empty1); OFFSET(__SF_EMPTY, stack_frame, empty1);
OFFSET(__SF_SIE_CONTROL, stack_frame, empty1[0]);
OFFSET(__SF_SIE_SAVEAREA, stack_frame, empty1[1]);
OFFSET(__SF_SIE_REASON, stack_frame, empty1[2]);
BLANK(); BLANK();
/* timeval/timezone offsets for use by vdso */ /* timeval/timezone offsets for use by vdso */
OFFSET(__VDSO_UPD_COUNT, vdso_data, tb_update_count); OFFSET(__VDSO_UPD_COUNT, vdso_data, tb_update_count);
......
...@@ -225,6 +225,7 @@ ENTRY(sie64a) ...@@ -225,6 +225,7 @@ ENTRY(sie64a)
jnz .Lsie_skip jnz .Lsie_skip
TSTMSK __LC_CPU_FLAGS,_CIF_FPU TSTMSK __LC_CPU_FLAGS,_CIF_FPU
jo .Lsie_skip # exit if fp/vx regs changed jo .Lsie_skip # exit if fp/vx regs changed
.Lsie_entry:
sie 0(%r14) sie 0(%r14)
.Lsie_skip: .Lsie_skip:
ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE
...@@ -1122,7 +1123,13 @@ cleanup_critical: ...@@ -1122,7 +1123,13 @@ cleanup_critical:
.quad .Lsie_done .quad .Lsie_done
.Lcleanup_sie: .Lcleanup_sie:
lg %r9,__SF_EMPTY(%r15) # get control block pointer cghi %r11,__LC_SAVE_AREA_ASYNC #Is this in normal interrupt?
je 1f
slg %r9,BASED(.Lsie_crit_mcck_start)
clg %r9,BASED(.Lsie_crit_mcck_length)
jh 1f
oi __LC_CPU_FLAGS+7, _CIF_MCCK_GUEST
1: lg %r9,__SF_EMPTY(%r15) # get control block pointer
ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE
lctlg %c1,%c1,__LC_USER_ASCE # load primary asce lctlg %c1,%c1,__LC_USER_ASCE # load primary asce
larl %r9,sie_exit # skip forward to sie_exit larl %r9,sie_exit # skip forward to sie_exit
...@@ -1307,6 +1314,10 @@ cleanup_critical: ...@@ -1307,6 +1314,10 @@ cleanup_critical:
.quad .Lsie_gmap .quad .Lsie_gmap
.Lsie_critical_length: .Lsie_critical_length:
.quad .Lsie_done - .Lsie_gmap .quad .Lsie_done - .Lsie_gmap
.Lsie_crit_mcck_start:
.quad .Lsie_entry
.Lsie_crit_mcck_length:
.quad .Lsie_skip - .Lsie_entry
#endif #endif
.section .rodata, "a" .section .rodata, "a"
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <asm/crw.h> #include <asm/crw.h>
#include <asm/switch_to.h> #include <asm/switch_to.h>
#include <asm/ctl_reg.h> #include <asm/ctl_reg.h>
#include <asm/asm-offsets.h>
#include <linux/kvm_host.h>
struct mcck_struct { struct mcck_struct {
unsigned int kill_task : 1; unsigned int kill_task : 1;
...@@ -274,12 +276,39 @@ static int notrace s390_validate_registers(union mci mci, int umode) ...@@ -274,12 +276,39 @@ static int notrace s390_validate_registers(union mci mci, int umode)
return kill_task; return kill_task;
} }
/*
* Backup the guest's machine check info to its description block
*/
static void notrace s390_backup_mcck_info(struct pt_regs *regs)
{
struct mcck_volatile_info *mcck_backup;
struct sie_page *sie_page;
/* r14 contains the sie block, which was set in sie64a */
struct kvm_s390_sie_block *sie_block =
(struct kvm_s390_sie_block *) regs->gprs[14];
if (sie_block == NULL)
/* Something's seriously wrong, stop system. */
s390_handle_damage();
sie_page = container_of(sie_block, struct sie_page, sie_block);
mcck_backup = &sie_page->mcck_info;
mcck_backup->mcic = S390_lowcore.mcck_interruption_code &
~(MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE);
mcck_backup->ext_damage_code = S390_lowcore.external_damage_code;
mcck_backup->failing_storage_address
= S390_lowcore.failing_storage_address;
}
#define MAX_IPD_COUNT 29 #define MAX_IPD_COUNT 29
#define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */ #define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */
#define ED_STP_ISLAND 6 /* External damage STP island check */ #define ED_STP_ISLAND 6 /* External damage STP island check */
#define ED_STP_SYNC 7 /* External damage STP sync check */ #define ED_STP_SYNC 7 /* External damage STP sync check */
#define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE)
/* /*
* machine check handler. * machine check handler.
*/ */
...@@ -291,6 +320,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -291,6 +320,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
struct mcck_struct *mcck; struct mcck_struct *mcck;
unsigned long long tmp; unsigned long long tmp;
union mci mci; union mci mci;
unsigned long mcck_dam_code;
nmi_enter(); nmi_enter();
inc_irq_stat(NMI_NMI); inc_irq_stat(NMI_NMI);
...@@ -301,7 +331,13 @@ void notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -301,7 +331,13 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
/* System damage -> stopping machine */ /* System damage -> stopping machine */
s390_handle_damage(); s390_handle_damage();
} }
if (mci.pd) {
/*
* Reinject the instruction processing damages' machine checks
* including Delayed Access Exception into the guest
* instead of damaging the host if they happen in the guest.
*/
if (mci.pd && !test_cpu_flag(CIF_MCCK_GUEST)) {
if (mci.b) { if (mci.b) {
/* Processing backup -> verify if we can survive this */ /* Processing backup -> verify if we can survive this */
u64 z_mcic, o_mcic, t_mcic; u64 z_mcic, o_mcic, t_mcic;
...@@ -345,6 +381,14 @@ void notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -345,6 +381,14 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
mcck->mcck_code = mci.val; mcck->mcck_code = mci.val;
set_cpu_flag(CIF_MCCK_PENDING); set_cpu_flag(CIF_MCCK_PENDING);
} }
/*
* Backup the machine check's info if it happens when the guest
* is running.
*/
if (test_cpu_flag(CIF_MCCK_GUEST))
s390_backup_mcck_info(regs);
if (mci.cd) { if (mci.cd) {
/* Timing facility damage */ /* Timing facility damage */
s390_handle_damage(); s390_handle_damage();
...@@ -358,15 +402,22 @@ void notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -358,15 +402,22 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
if (mcck->stp_queue) if (mcck->stp_queue)
set_cpu_flag(CIF_MCCK_PENDING); set_cpu_flag(CIF_MCCK_PENDING);
} }
if (mci.se)
/* Storage error uncorrected */ /*
s390_handle_damage(); * Reinject storage related machine checks into the guest if they
if (mci.ke) * happen when the guest is running.
/* Storage key-error uncorrected */ */
s390_handle_damage(); if (!test_cpu_flag(CIF_MCCK_GUEST)) {
if (mci.ds && mci.fa) if (mci.se)
/* Storage degradation */ /* Storage error uncorrected */
s390_handle_damage(); s390_handle_damage();
if (mci.ke)
/* Storage key-error uncorrected */
s390_handle_damage();
if (mci.ds && mci.fa)
/* Storage degradation */
s390_handle_damage();
}
if (mci.cp) { if (mci.cp) {
/* Channel report word pending */ /* Channel report word pending */
mcck->channel_report = 1; mcck->channel_report = 1;
...@@ -377,6 +428,19 @@ void notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -377,6 +428,19 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
mcck->warning = 1; mcck->warning = 1;
set_cpu_flag(CIF_MCCK_PENDING); set_cpu_flag(CIF_MCCK_PENDING);
} }
/*
* If there are only Channel Report Pending and External Damage
* machine checks, they will not be reinjected into the guest
* because they refer to host conditions only.
*/
mcck_dam_code = (mci.val & MCIC_SUBCLASS_MASK);
if (test_cpu_flag(CIF_MCCK_GUEST) &&
(mcck_dam_code & MCCK_CODE_NO_GUEST) != mcck_dam_code) {
/* Set exit reason code for host's later handling */
*((long *)(regs->gprs[15] + __SF_SIE_REASON)) = -EINTR;
}
clear_cpu_flag(CIF_MCCK_GUEST);
nmi_exit(); nmi_exit();
} }
......
...@@ -2069,6 +2069,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, ...@@ -2069,6 +2069,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
if (!vcpu) if (!vcpu)
goto out; goto out;
BUILD_BUG_ON(sizeof(struct sie_page) != 4096);
sie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL); sie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL);
if (!sie_page) if (!sie_page)
goto out_free_cpu; goto out_free_cpu;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment