s390x/kvm/watchdog

1. Implement a diag288 based watchdog
 2. Fix virtio-ccw BIOS for gcc >= 4.9
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.14 (GNU/Linux)
 
 iQIcBAABAgAGBQJVfri5AAoJEBF7vIC1phx8ME4P/AgMYstY5sjzmSYKAXLhwmZl
 vDaxZ1MqoRZQfzPWXcPGavNFb9lnebLkedWO4FvYWWoVRxUHThKF2d5PDz8qsS7C
 lNhp0YNiMHcvSv8mlAr9TVP3ssKbp0YFyhZAIEtjlAesnbHl0URfPUulx0TpKHsg
 EqEiwFPB+eUHD1GiBa+x7bTCnyp8Ppn9SrGItJR0Mq7AjLzvjtwATPZbLk+I6mwN
 Nqd0uhYScbi6NX4UlifzaYYePjAQmN2ZhLwzuasqHhhJtAm8M6EPXkwxb9X5elWJ
 cZmCEkYMD+j5G6Lqm3ZV44g1fSM9k4ZctcOUpQzPmS+sDj48ydDDkgy/hiY/Fu6e
 Xf3Ti6ChSQMjQc9vcYWHtdAt+rvYAxOqTK//hHp87MADsREYjhVXVBMubJZ5h+z0
 eEtuKo6npZg1AD8lFdCMEyvqWHqldVYiZfJwTBoCbX26bEpkbQhy11PjD7Sm3lP0
 EVad06C1Rv4Gr6uiL+4Pqulm6G8CqATiLMUrE9VkI7WhtwOa81OQS6v+dlVIERLF
 ueKt7bhCBIQrmdsAsNlC5nvP39pCkkBjogdw+S7UttTFA9KEKr8gMjHVAdNNk/Or
 OBi1YBJOCc2CcE/0mVpC8okgprOP3AOjPGn+wmlRNw2s3TOlmVN/rz/d6lr/rPFV
 VIpdMY0RuQSQY36qUdEo
 =WVga
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/borntraeger/tags/s390x-20150615' into staging

s390x/kvm/watchdog

1. Implement a diag288 based watchdog
2. Fix virtio-ccw BIOS for gcc >= 4.9

# gpg: Signature made Mon Jun 15 12:36:25 2015 BST using RSA key ID B5A61C7C
# gpg: Good signature from "Christian Borntraeger (IBM) <borntraeger@de.ibm.com>"

* remotes/borntraeger/tags/s390x-20150615:
  s390/bios: build with -fdelete-null-pointer-checks
  watchdog: Add new Virtual Watchdog action INJECT-NMI
  nmi: Implement inject_nmi() for non-monitor context use
  s390x/watchdog: diag288 migration support
  s390x/kvm: diag288 instruction interception and handling
  s390x/watchdog: introduce diag288 watchdog device
  watchdog: change option wording to allow for more watchdogs

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2015-06-15 13:24:50 +01:00
commit 46bca5404b
13 changed files with 262 additions and 11 deletions

View file

@ -4,3 +4,4 @@ CONFIG_VIRTIO=y
CONFIG_SCLPCONSOLE=y
CONFIG_S390_FLIC=y
CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
CONFIG_WDT_DIAG288=y

View file

@ -21,6 +21,7 @@
#include "hw/nmi.h"
#include "qapi/qmp/qerror.h"
#include "monitor/monitor.h"
struct do_nmi_s {
int cpu_index;
@ -70,6 +71,25 @@ void nmi_monitor_handle(int cpu_index, Error **errp)
}
}
void inject_nmi(void)
{
#if defined(TARGET_I386)
CPUState *cs;
CPU_FOREACH(cs) {
X86CPU *cpu = X86_CPU(cs);
if (!cpu->apic_state) {
cpu_interrupt(cs, CPU_INTERRUPT_NMI);
} else {
apic_deliver_nmi(cpu->apic_state);
}
}
#else
nmi_monitor_handle(0, NULL);
#endif
}
static const TypeInfo nmi_info = {
.name = TYPE_NMI,
.parent = TYPE_INTERFACE,

View file

@ -1,3 +1,4 @@
common-obj-y += watchdog.o
common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o

View file

@ -27,6 +27,7 @@
#include "sysemu/sysemu.h"
#include "sysemu/watchdog.h"
#include "qapi-event.h"
#include "hw/nmi.h"
/* Possible values for action parameter. */
#define WDT_RESET 1 /* Hard reset. */
@ -35,6 +36,7 @@
#define WDT_PAUSE 4 /* Pause. */
#define WDT_DEBUG 5 /* Prints a message and continues running. */
#define WDT_NONE 6 /* Do nothing. */
#define WDT_NMI 7 /* Inject nmi into the guest */
static int watchdog_action = WDT_RESET;
static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
@ -95,6 +97,8 @@ int select_watchdog_action(const char *p)
watchdog_action = WDT_DEBUG;
else if (strcasecmp(p, "none") == 0)
watchdog_action = WDT_NONE;
else if (strcasecmp(p, "inject-nmi") == 0)
watchdog_action = WDT_NMI;
else
return -1;
@ -138,5 +142,11 @@ void watchdog_perform_action(void)
case WDT_NONE:
qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort);
break;
case WDT_NMI:
qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI,
&error_abort);
inject_nmi();
break;
}
}

122
hw/watchdog/wdt_diag288.c Normal file
View file

@ -0,0 +1,122 @@
/*
* watchdog device diag288 support
*
* Copyright IBM, Corp. 2015
*
* Authors:
* Xu Wang <gesaint@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at your
* option) any later version. See the COPYING file in the top-level directory.
*
*/
#include "sysemu/watchdog.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "hw/watchdog/wdt_diag288.h"
static WatchdogTimerModel model = {
.wdt_name = TYPE_WDT_DIAG288,
.wdt_description = "diag288 device for s390x platform",
};
static const VMStateDescription vmstate_diag288 = {
.name = "vmstate_diag288",
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(timer, DIAG288State),
VMSTATE_BOOL(enabled, DIAG288State),
VMSTATE_END_OF_LIST()
}
};
static void wdt_diag288_reset(DeviceState *dev)
{
DIAG288State *diag288 = DIAG288(dev);
diag288->enabled = false;
timer_del(diag288->timer);
}
static void diag288_timer_expired(void *dev)
{
qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
watchdog_perform_action();
wdt_diag288_reset(dev);
}
static int wdt_diag288_handle_timer(DIAG288State *diag288,
uint64_t func, uint64_t timeout)
{
switch (func) {
case WDT_DIAG288_INIT:
diag288->enabled = true;
/* fall through */
case WDT_DIAG288_CHANGE:
if (!diag288->enabled) {
return -1;
}
timer_mod(diag288->timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
timeout * get_ticks_per_sec());
break;
case WDT_DIAG288_CANCEL:
if (!diag288->enabled) {
return -1;
}
diag288->enabled = false;
timer_del(diag288->timer);
break;
default:
return -1;
}
return 0;
}
static void wdt_diag288_realize(DeviceState *dev, Error **errp)
{
DIAG288State *diag288 = DIAG288(dev);
diag288->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, diag288_timer_expired,
dev);
}
static void wdt_diag288_unrealize(DeviceState *dev, Error **errp)
{
DIAG288State *diag288 = DIAG288(dev);
timer_del(diag288->timer);
timer_free(diag288->timer);
}
static void wdt_diag288_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
DIAG288Class *diag288 = DIAG288_CLASS(klass);
dc->realize = wdt_diag288_realize;
dc->unrealize = wdt_diag288_unrealize;
dc->reset = wdt_diag288_reset;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->vmsd = &vmstate_diag288;
diag288->handle_timer = wdt_diag288_handle_timer;
}
static const TypeInfo wdt_diag288_info = {
.class_init = wdt_diag288_class_init,
.parent = TYPE_DEVICE,
.name = TYPE_WDT_DIAG288,
.instance_size = sizeof(DIAG288State),
.class_size = sizeof(DIAG288Class),
};
static void wdt_diag288_register_types(void)
{
watchdog_add_model(&model);
type_register_static(&wdt_diag288_info);
}
type_init(wdt_diag288_register_types)

View file

@ -45,5 +45,6 @@ typedef struct NMIClass {
} NMIClass;
void nmi_monitor_handle(int cpu_index, Error **errp);
void inject_nmi(void);
#endif /* NMI_H */

View file

@ -0,0 +1,36 @@
#ifndef WDT_DIAG288_H
#define WDT_DIAG288_H
#include "hw/qdev.h"
#define TYPE_WDT_DIAG288 "diag288"
#define DIAG288(obj) \
OBJECT_CHECK(DIAG288State, (obj), TYPE_WDT_DIAG288)
#define DIAG288_CLASS(klass) \
OBJECT_CLASS_CHECK(DIAG288Class, (klass), TYPE_WDT_DIAG288)
#define DIAG288_GET_CLASS(obj) \
OBJECT_GET_CLASS(DIAG288Class, (obj), TYPE_WDT_DIAG288)
#define WDT_DIAG288_INIT 0
#define WDT_DIAG288_CHANGE 1
#define WDT_DIAG288_CANCEL 2
typedef struct DIAG288State {
/*< private >*/
DeviceState parent_obj;
QEMUTimer *timer;
bool enabled;
/*< public >*/
} DIAG288State;
typedef struct DIAG288Class {
/*< private >*/
DeviceClass parent_class;
/*< public >*/
int (*handle_timer)(DIAG288State *dev,
uint64_t func, uint64_t timeout);
} DIAG288Class;
#endif /* WDT_DIAG288_H */

View file

@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
.PHONY : all clean build-all
OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o
CFLAGS += -fPIE -fno-stack-protector -ffreestanding
CFLAGS += -fPIE -fno-stack-protector -ffreestanding -fno-delete-null-pointer-checks
LDFLAGS += -Wl,-pie -nostdlib
build-all: s390-ccw.img

View file

@ -3746,10 +3746,14 @@
#
# @none: nothing is done
#
# @inject-nmi: a non-maskable interrupt is injected into the first VCPU (all
# VCPUS on x86) (since 2.4)
#
# Since: 2.1
##
{ 'enum': 'WatchdogExpirationAction',
'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none' ] }
'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none',
'inject-nmi' ] }
##
# @IoOperationType

View file

@ -3153,7 +3153,7 @@ when the shift value is high (how high depends on the host machine).
ETEXI
DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
"-watchdog i6300esb|ib700\n" \
"-watchdog model\n" \
" enable virtual hardware watchdog [default=none]\n",
QEMU_ARCH_ALL)
STEXI
@ -3161,16 +3161,24 @@ STEXI
@findex -watchdog
Create a virtual hardware watchdog device. Once enabled (by a guest
action), the watchdog must be periodically polled by an agent inside
the guest or else the guest will be restarted.
the guest or else the guest will be restarted. Choose a model for
which your guest has drivers.
The @var{model} is the model of hardware watchdog to emulate. Choices
for model are: @code{ib700} (iBASE 700) which is a very simple ISA
watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
controller hub) which is a much more featureful PCI-based dual-timer
watchdog. Choose a model for which your guest has drivers.
Use @code{-watchdog help} to list available hardware models. Only one
The @var{model} is the model of hardware watchdog to emulate. Use
@code{-watchdog help} to list available hardware models. Only one
watchdog can be enabled for a guest.
The following models may be available:
@table @option
@item ib700
iBASE 700 is a very simple ISA watchdog with a single timer.
@item i6300esb
Intel 6300ESB I/O controller hub is a much more featureful PCI-based
dual-timer watchdog.
@item diag288
A virtual watchdog for s390x backed by the diagnose 288 hypercall
(currently KVM only).
@end table
ETEXI
DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \

View file

@ -1100,6 +1100,7 @@ uint32_t set_cc_nz_f128(float128 v);
/* misc_helper.c */
#ifndef CONFIG_USER_ONLY
int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3);
void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3);
#endif
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);

View file

@ -98,6 +98,7 @@
#define PRIV_E3_MPCIFC 0xd0
#define PRIV_E3_STPCIFC 0xd4
#define DIAG_TIMEREVENT 0x288
#define DIAG_IPL 0x308
#define DIAG_KVM_HYPERCALL 0x500
#define DIAG_KVM_BREAKPOINT 0x501
@ -1267,6 +1268,20 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
return ret;
}
static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run)
{
uint64_t r1, r3;
int rc;
cpu_synchronize_state(CPU(cpu));
r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
r3 = run->s390_sieic.ipa & 0x000f;
rc = handle_diag_288(&cpu->env, r1, r3);
if (rc) {
enter_pgmcheck(cpu, PGM_SPECIFICATION);
}
}
static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
{
uint64_t r1, r3;
@ -1306,6 +1321,9 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
*/
func_code = decode_basedisp_rs(&cpu->env, ipb, NULL) & DIAG_KVM_CODE_MASK;
switch (func_code) {
case DIAG_TIMEREVENT:
kvm_handle_diag_288(cpu, run);
break;
case DIAG_IPL:
kvm_handle_diag_308(cpu, run);
break;

View file

@ -30,6 +30,7 @@
#include <linux/kvm.h>
#endif
#include "exec/cpu_ldst.h"
#include "hw/watchdog/wdt_diag288.h"
#if !defined(CONFIG_USER_ONLY)
#include "sysemu/cpus.h"
@ -153,6 +154,34 @@ static int load_normal_reset(S390CPU *cpu)
return 0;
}
int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
{
uint64_t func = env->regs[r1];
uint64_t timeout = env->regs[r1 + 1];
uint64_t action = env->regs[r3];
Object *obj;
DIAG288State *diag288;
DIAG288Class *diag288_class;
if (r1 % 2 || action != 0) {
return -1;
}
/* Timeout must be more than 15 seconds except for timer deletion */
if (func != WDT_DIAG288_CANCEL && timeout < 15) {
return -1;
}
obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
if (!obj) {
return -1;
}
diag288 = DIAG288(obj);
diag288_class = DIAG288_GET_CLASS(diag288);
return diag288_class->handle_timer(diag288, func, timeout);
}
#define DIAG_308_RC_OK 0x0001
#define DIAG_308_RC_NO_CONF 0x0102
#define DIAG_308_RC_INVALID 0x0402