mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
s390,kvm: provide plumbing for machines checks when running guests
This provides the basic plumbing for handling machine checks when running guests -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJZU4QPAAoJEBF7vIC1phx8GZsP/2P4nxWXBj0NS/dNq54/u7HU Va/zHIG7nUX81WZi8OCkPRlvb1RlcgNpIdw3Ar+BueFE6/qwVWBSdstVJCg6JSn4 L8T1srSeV6yQEPq1/I9S8ERYtbC8bOC3dDF6g+KyaKYnICjq5yC01+86MKSVfLTI vFMPWY/PPCgECtXHjGpWBW6HjofRH3/H+XQbxaoTUyHKwWKdtvWer9K2V7Mc/Cf8 XsyLY2Xq0Y5MBsJs+71Qw8+0R041Et5I3H7Od9lIc3SFYNoenQpk5oTtsujMtDG1 ccMPZKErYI4wHE3Hy1ozK+MdFNbepUk3RBI3oXU25tpFPG3OPuksnOqCVN/iZmm+ le9RuUi9WOOsuygPj2dsnx5v+aheedEcYWqvQ/qrNlP3pXNcpl+8waM6eke8HyCK 1JKcqqGKBNX5wKNE9b5sRTHINWK12EVCQyVrgLlZaXoXLa40NpJPjtV27vr3ttVl WmGYgwMUTo15Rdr0NSJlXl8iCgIFtWMHvuRhIgp8pBuWWb28zr6aX4w++JPwOOMZ e4rzn55giCBDnjjDFQK2Knv5XxwnMKafYMxZXfC8gLr5ELjnI6vZDN+1zhT5L2S9 uXd8l6rLN2qik57RzPV6YEDS0iybZnx5HF/ZPrNoFigJpdD7/0jFS5K5N0i+AhV5 UQmGhSGnI7Teguc45mHT =CTzL -----END PGP SIGNATURE----- Merge tag 'nmiforkvm' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into kernelorgnext s390,kvm: provide plumbing for machines checks when running guests This provides the basic plumbing for handling machine checks when running guests
This commit is contained in:
commit
aec3b2c5f9
7 changed files with 115 additions and 12 deletions
|
@ -109,6 +109,20 @@ struct esca_block {
|
||||||
struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS];
|
struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
@ -266,7 +280,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 */
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 @@
|
||||||
#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 @@
|
||||||
#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)
|
||||||
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)
|
||||||
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
|
||||||
|
@ -1104,7 +1105,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
|
||||||
|
@ -1289,6 +1296,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 @@
|
||||||
#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)
|
||||||
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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2405,6 +2405,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;
|
||||||
|
|
Loading…
Reference in a new issue