* Some CSS related fixes

* Storage key related fixes
 * Test SIGILL and SIGSEGV handling in usermode emulation
 * Fix SETPREFIX instruction
 * Replace PAGE_SIZE, PAGE_SHIFT and PAGE_MASK to fix Alpine compilation
 * Add more feature to gen16 default model
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAmE3ZBoRHHRodXRoQHJl
 ZGhhdC5jb20ACgkQLtnXdP5wLbXKjw/9GE/51PH4y5if9tJq8PPASy6ADMZLxBCJ
 W2oXQVd5ZgOkRS1epKI8cLwaKX6TmpY0E3Tfa9itOFVhpRPe4xjWAXQZLSOKsohe
 qF9cMEj0FmNlnbqlRsxXys0LsUOsO7XhVD2Nf3XKOuUVbptn7v2YF29pXc/v3s5j
 qaMeRtmQsLllgERC1WcbPlSc0D59HAZqn/WBsPlYg2fUJ0vhZSrWVqZavpy1RDlI
 qAcb6SBm0b09OZ0Unlb3HKWaD9tqt8oBRmyihWlXcnKYcho5A5bD3P989Qe6/Krw
 6ANcjTYhNFlsOItcsB+nOwICp2dgHYODm5L6mHT5FZm/cPuW+2ctbm3rsxsvfT+6
 EvhB5CLWAUY8kwsDnehZFHZ8jvW6WbwZRyuEtE4/W5qo3heP3vt6b5ctURTiWpC7
 gwFjX8g7Lv42kLNUqJHCPXPQB1ozG+DyUZPL3buiHvccnwhcRd2nJMaGRFxa4BLE
 xg4J9cOBlz1VlX9rDTg9Y8EwMcQq+4HpEjHrUqWbn+mGq1v9gUvHJsNMX9hARyhz
 dWEEY+tA9Jp76hOHaXg1ZZcTvY/H80za3qe8IXNGzjetBWi87CqJtKnBMkHo6ydn
 RlgXYC4RaVFoTeZjPh/Oc0CYusz4olBsnvtpGhIQDqw75dewtXQ2dsJsHHlfU+wc
 HCt3lcnxAWk=
 =dh2k
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/thuth-gitlab/tags/s390x-pull-request-2021-09-07' into staging

* Some CSS related fixes
* Storage key related fixes
* Test SIGILL and SIGSEGV handling in usermode emulation
* Fix SETPREFIX instruction
* Replace PAGE_SIZE, PAGE_SHIFT and PAGE_MASK to fix Alpine compilation
* Add more feature to gen16 default model

# gpg: Signature made Tue 07 Sep 2021 14:07:38 BST
# gpg:                using RSA key 27B88847EEE0250118F3EAB92ED9D774FE702DB5
# gpg:                issuer "thuth@redhat.com"
# gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full]
# gpg:                 aka "Thomas Huth <thuth@redhat.com>" [full]
# gpg:                 aka "Thomas Huth <huth@tuxfamily.org>" [full]
# gpg:                 aka "Thomas Huth <th.huth@posteo.de>" [unknown]
# Primary key fingerprint: 27B8 8847 EEE0 2501 18F3  EAB9 2ED9 D774 FE70 2DB5

* remotes/thuth-gitlab/tags/s390x-pull-request-2021-09-07:
  s390x/cpumodel: Add more feature to gen16 default model
  s390x: Replace PAGE_SIZE, PAGE_SHIFT and PAGE_MASK
  hw/s390x/s390-skeys: lazy storage key enablement under TCG
  hw/s390x/s390-skeys: rename skeys_enabled to skeys_are_enabled
  hw/s390x/s390-skeys: check if an address is valid before dumping the key
  hw/s390x/s390-skeys: use memory mapping to detect which storage keys to dump
  hw/s390x/s390-skeys: use memory mapping to detect which storage keys to migrate
  s390x/mmu_helper: avoid setting the storage key if nothing changed
  s390x/mmu_helper: move address validation into mmu_translate*()
  s390x/mmu_helper: fixup mmu_translate() documentation
  s390x/mmu_helper: no need to pass access type to mmu_translate_asce()
  s390x/tcg: check for addressing exceptions for RRBE, SSKE and ISKE
  s390x/tcg: convert real to absolute address for RRBE, SSKE and ISKE
  s390x/tcg: fix ignoring bit 63 when setting the storage key in SSKE
  s390x/tcg: wrap address for RRBE
  s390x/ioinst: Fix wrong MSCH alignment check on little endian
  s390x/tcg: fix and optimize SPX (SET PREFIX)
  tests/tcg/s390x: Test SIGILL and SIGSEGV handling
  css: fix actl handling for unit exceptions
  vfio-ccw: forward halt/clear errors

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-09-07 17:46:13 +01:00
commit abf7aee72e
22 changed files with 626 additions and 150 deletions

View File

@ -1206,23 +1206,53 @@ static void sch_handle_start_func_virtual(SubchDev *sch)
}
static void sch_handle_halt_func_passthrough(SubchDev *sch)
static IOInstEnding sch_handle_halt_func_passthrough(SubchDev *sch)
{
int ret;
ret = s390_ccw_halt(sch);
if (ret == -ENOSYS) {
sch_handle_halt_func(sch);
return IOINST_CC_EXPECTED;
}
/*
* Some conditions may have been detected prior to starting the halt
* function; map them to the correct cc.
* Note that we map both -ENODEV and -EACCES to cc 3 (there's not really
* anything else we can do.)
*/
switch (ret) {
case -EBUSY:
return IOINST_CC_BUSY;
case -ENODEV:
case -EACCES:
return IOINST_CC_NOT_OPERATIONAL;
default:
return IOINST_CC_EXPECTED;
}
}
static void sch_handle_clear_func_passthrough(SubchDev *sch)
static IOInstEnding sch_handle_clear_func_passthrough(SubchDev *sch)
{
int ret;
ret = s390_ccw_clear(sch);
if (ret == -ENOSYS) {
sch_handle_clear_func(sch);
return IOINST_CC_EXPECTED;
}
/*
* Some conditions may have been detected prior to starting the clear
* function; map them to the correct cc.
* Note that we map both -ENODEV and -EACCES to cc 3 (there's not really
* anything else we can do.)
*/
switch (ret) {
case -ENODEV:
case -EACCES:
return IOINST_CC_NOT_OPERATIONAL;
default:
return IOINST_CC_EXPECTED;
}
}
@ -1265,9 +1295,9 @@ IOInstEnding do_subchannel_work_passthrough(SubchDev *sch)
SCHIB *schib = &sch->curr_status;
if (schib->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) {
sch_handle_clear_func_passthrough(sch);
return sch_handle_clear_func_passthrough(sch);
} else if (schib->scsw.ctrl & SCSW_FCTL_HALT_FUNC) {
sch_handle_halt_func_passthrough(sch);
return sch_handle_halt_func_passthrough(sch);
} else if (schib->scsw.ctrl & SCSW_FCTL_START_FUNC) {
return sch_handle_start_func_passthrough(sch);
}

View File

@ -330,7 +330,7 @@ static unsigned int calc_sx(dma_addr_t ptr)
static unsigned int calc_px(dma_addr_t ptr)
{
return ((unsigned long) ptr >> PAGE_SHIFT) & ZPCI_PT_MASK;
return ((unsigned long) ptr >> TARGET_PAGE_BITS) & ZPCI_PT_MASK;
}
static uint64_t get_rt_sto(uint64_t entry)
@ -506,7 +506,7 @@ uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr,
int8_t ett = 1;
uint16_t error = 0;
entry->iova = addr & PAGE_MASK;
entry->iova = addr & TARGET_PAGE_MASK;
entry->translated_addr = 0;
entry->perm = IOMMU_RW;
@ -526,7 +526,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
{
S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr);
S390IOTLBEntry *entry;
uint64_t iova = addr & PAGE_MASK;
uint64_t iova = addr & TARGET_PAGE_MASK;
uint16_t error = 0;
IOMMUTLBEntry ret = {
.target_as = &address_space_memory,
@ -562,7 +562,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
ret.perm = entry->perm;
} else {
ret.iova = iova;
ret.addr_mask = ~PAGE_MASK;
ret.addr_mask = ~TARGET_PAGE_MASK;
ret.perm = IOMMU_NONE;
}
@ -868,7 +868,7 @@ static int s390_pci_msix_init(S390PCIBusDevice *pbdev)
name = g_strdup_printf("msix-s390-%04x", pbdev->uid);
memory_region_init_io(&pbdev->msix_notify_mr, OBJECT(pbdev),
&s390_msi_ctrl_ops, pbdev, name, PAGE_SIZE);
&s390_msi_ctrl_ops, pbdev, name, TARGET_PAGE_SIZE);
memory_region_add_subregion(&pbdev->iommu->mr,
pbdev->pci_group->zpci_group.msia,
&pbdev->msix_notify_mr);

View File

@ -613,7 +613,7 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
.iova = entry->iova,
.translated_addr = entry->translated_addr,
.perm = entry->perm,
.addr_mask = ~PAGE_MASK,
.addr_mask = ~TARGET_PAGE_MASK,
},
};
@ -640,7 +640,7 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
cache = g_new(S390IOTLBEntry, 1);
cache->iova = entry->iova;
cache->translated_addr = entry->translated_addr;
cache->len = PAGE_SIZE;
cache->len = TARGET_PAGE_SIZE;
cache->perm = entry->perm;
g_hash_table_replace(iommu->iotlb, &cache->iova, cache);
dec_dma_avail(iommu);
@ -725,8 +725,8 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
while (entry.iova < start && entry.iova < end &&
(dma_avail > 0 || entry.perm == IOMMU_NONE)) {
dma_avail = s390_pci_update_iotlb(iommu, &entry);
entry.iova += PAGE_SIZE;
entry.translated_addr += PAGE_SIZE;
entry.iova += TARGET_PAGE_SIZE;
entry.translated_addr += TARGET_PAGE_SIZE;
}
}
err:

View File

@ -15,7 +15,7 @@
#include "qemu/error-report.h"
#include "qemu/module.h"
static int kvm_s390_skeys_enabled(S390SKeysState *ss)
static bool kvm_s390_skeys_are_enabled(S390SKeysState *ss)
{
S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
uint8_t single_key;
@ -57,7 +57,7 @@ static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data)
S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
skeyclass->skeys_enabled = kvm_s390_skeys_enabled;
skeyclass->skeys_are_enabled = kvm_s390_skeys_are_enabled;
skeyclass->get_skeys = kvm_s390_skeys_get;
skeyclass->set_skeys = kvm_s390_skeys_set;

View File

@ -17,6 +17,8 @@
#include "qapi/qapi-commands-misc-target.h"
#include "qapi/qmp/qdict.h"
#include "qemu/error-report.h"
#include "sysemu/memory_mapping.h"
#include "exec/address-spaces.h"
#include "sysemu/kvm.h"
#include "migration/qemu-file-types.h"
#include "migration/register.h"
@ -80,11 +82,18 @@ void hmp_info_skeys(Monitor *mon, const QDict *qdict)
int r;
/* Quick check to see if guest is using storage keys*/
if (!skeyclass->skeys_enabled(ss)) {
if (!skeyclass->skeys_are_enabled(ss)) {
monitor_printf(mon, "Error: This guest is not using storage keys\n");
return;
}
if (!address_space_access_valid(&address_space_memory,
addr & TARGET_PAGE_MASK, TARGET_PAGE_SIZE,
false, MEMTXATTRS_UNSPECIFIED)) {
monitor_printf(mon, "Error: The given address is not valid\n");
return;
}
r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (r < 0) {
monitor_printf(mon, "Error: %s\n", strerror(-r));
@ -109,18 +118,17 @@ void qmp_dump_skeys(const char *filename, Error **errp)
{
S390SKeysState *ss = s390_get_skeys_device();
S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
MachineState *ms = MACHINE(qdev_get_machine());
const uint64_t total_count = ms->ram_size / TARGET_PAGE_SIZE;
uint64_t handled_count = 0, cur_count;
GuestPhysBlockList guest_phys_blocks;
GuestPhysBlock *block;
uint64_t pages, gfn;
Error *lerr = NULL;
vaddr cur_gfn = 0;
uint8_t *buf;
int ret;
int fd;
FILE *f;
/* Quick check to see if guest is using storage keys*/
if (!skeyclass->skeys_enabled(ss)) {
if (!skeyclass->skeys_are_enabled(ss)) {
error_setg(errp, "This guest is not using storage keys - "
"nothing to dump");
return;
@ -144,53 +152,86 @@ void qmp_dump_skeys(const char *filename, Error **errp)
goto out;
}
/* we'll only dump initial memory for now */
while (handled_count < total_count) {
/* Calculate how many keys to ask for & handle overflow case */
cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE);
assert(qemu_mutex_iothread_locked());
guest_phys_blocks_init(&guest_phys_blocks);
guest_phys_blocks_append(&guest_phys_blocks);
ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf);
if (ret < 0) {
error_setg(errp, "get_keys error %d", ret);
goto out_free;
QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
gfn = block->target_start / TARGET_PAGE_SIZE;
pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
while (pages) {
const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
ret = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
if (ret < 0) {
error_setg_errno(errp, -ret, "get_keys error");
goto out_free;
}
/* write keys to stream */
write_keys(f, buf, gfn, cur_pages, &lerr);
if (lerr) {
goto out_free;
}
gfn += cur_pages;
pages -= cur_pages;
}
/* write keys to stream */
write_keys(f, buf, cur_gfn, cur_count, &lerr);
if (lerr) {
goto out_free;
}
cur_gfn += cur_count;
handled_count += cur_count;
}
out_free:
guest_phys_blocks_free(&guest_phys_blocks);
error_propagate(errp, lerr);
g_free(buf);
out:
fclose(f);
}
static void qemu_s390_skeys_init(Object *obj)
static bool qemu_s390_skeys_are_enabled(S390SKeysState *ss)
{
QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj);
MachineState *machine = MACHINE(qdev_get_machine());
QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss);
skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE;
skeys->keydata = g_malloc0(skeys->key_count);
/* Lockless check is sufficient. */
return !!skeys->keydata;
}
static int qemu_s390_skeys_enabled(S390SKeysState *ss)
static bool qemu_s390_enable_skeys(S390SKeysState *ss)
{
return 1;
QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss);
static gsize initialized;
if (likely(skeys->keydata)) {
return true;
}
/*
* TODO: Modern Linux doesn't use storage keys unless running KVM guests
* that use storage keys. Therefore, we keep it simple for now.
*
* 1) We should initialize to "referenced+changed" for an initial
* over-indication. Let's avoid touching megabytes of data for now and
* assume that any sane user will issue a storage key instruction before
* actually relying on this data.
* 2) Relying on ram_size and allocating a big array is ugly. We should
* allocate and manage storage key data per RAMBlock or optimally using
* some sparse data structure.
* 3) We only ever have a single S390SKeysState, so relying on
* g_once_init_enter() is good enough.
*/
if (g_once_init_enter(&initialized)) {
MachineState *machine = MACHINE(qdev_get_machine());
skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE;
skeys->keydata = g_malloc0(skeys->key_count);
g_once_init_leave(&initialized, 1);
}
return false;
}
/*
* TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get
* will have to make sure that the given gfn belongs to a memory region and not
* a memory hole.
*/
static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
uint64_t count, uint8_t *keys)
{
@ -198,9 +239,10 @@ static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
int i;
/* Check for uint64 overflow and access beyond end of key data */
if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
error_report("Error: Setting storage keys for page beyond the end "
"of memory: gfn=%" PRIx64 " count=%" PRId64,
if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count ||
start_gfn + count < count)) {
error_report("Error: Setting storage keys for pages with unallocated "
"storage key memory: gfn=%" PRIx64 " count=%" PRId64,
start_gfn, count);
return -EINVAL;
}
@ -218,9 +260,10 @@ static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
int i;
/* Check for uint64 overflow and access beyond end of key data */
if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
error_report("Error: Getting storage keys for page beyond the end "
"of memory: gfn=%" PRIx64 " count=%" PRId64,
if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count ||
start_gfn + count < count)) {
error_report("Error: Getting storage keys for pages with unallocated "
"storage key memory: gfn=%" PRIx64 " count=%" PRId64,
start_gfn, count);
return -EINVAL;
}
@ -236,7 +279,8 @@ static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
skeyclass->skeys_enabled = qemu_s390_skeys_enabled;
skeyclass->skeys_are_enabled = qemu_s390_skeys_are_enabled;
skeyclass->enable_skeys = qemu_s390_enable_skeys;
skeyclass->get_skeys = qemu_s390_skeys_get;
skeyclass->set_skeys = qemu_s390_skeys_set;
@ -247,7 +291,6 @@ static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
static const TypeInfo qemu_s390_skeys_info = {
.name = TYPE_QEMU_S390_SKEYS,
.parent = TYPE_S390_SKEYS,
.instance_init = qemu_s390_skeys_init,
.instance_size = sizeof(QEMUS390SKeysState),
.class_init = qemu_s390_skeys_class_init,
.class_size = sizeof(S390SKeysClass),
@ -257,14 +300,13 @@ static void s390_storage_keys_save(QEMUFile *f, void *opaque)
{
S390SKeysState *ss = S390_SKEYS(opaque);
S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
MachineState *ms = MACHINE(qdev_get_machine());
uint64_t pages_left = ms->ram_size / TARGET_PAGE_SIZE;
uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS;
vaddr cur_gfn = 0;
GuestPhysBlockList guest_phys_blocks;
GuestPhysBlock *block;
uint64_t pages, gfn;
int error = 0;
uint8_t *buf;
if (!skeyclass->skeys_enabled(ss)) {
if (!skeyclass->skeys_are_enabled(ss)) {
goto end_stream;
}
@ -274,36 +316,52 @@ static void s390_storage_keys_save(QEMUFile *f, void *opaque)
goto end_stream;
}
/* We only support initial memory. Standby memory is not handled yet. */
qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS);
qemu_put_be64(f, pages_left);
guest_phys_blocks_init(&guest_phys_blocks);
guest_phys_blocks_append(&guest_phys_blocks);
while (pages_left) {
read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE);
/* Send each contiguous physical memory range separately. */
QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
if (!error) {
error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf);
if (error) {
/*
* If error: we want to fill the stream with valid data instead
* of stopping early so we pad the stream with 0x00 values and
* use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the
* reading side.
*/
error_report("S390_GET_KEYS error %d", error);
memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
eos = S390_SKEYS_SAVE_FLAG_ERROR;
gfn = block->target_start / TARGET_PAGE_SIZE;
pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
qemu_put_be64(f, block->target_start | S390_SKEYS_SAVE_FLAG_SKEYS);
qemu_put_be64(f, pages);
while (pages) {
const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
if (!error) {
error = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
if (error) {
/*
* Create a valid stream with all 0x00 and indicate
* S390_SKEYS_SAVE_FLAG_ERROR to the destination.
*/
error_report("S390_GET_KEYS error %d", error);
memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
}
}
qemu_put_buffer(f, buf, cur_pages);
gfn += cur_pages;
pages -= cur_pages;
}
qemu_put_buffer(f, buf, read_count);
cur_gfn += read_count;
pages_left -= read_count;
if (error) {
break;
}
}
guest_phys_blocks_free(&guest_phys_blocks);
g_free(buf);
end_stream:
qemu_put_be64(f, eos);
if (error) {
qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_ERROR);
} else {
qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_EOS);
}
}
static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
@ -312,6 +370,14 @@ static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
int ret = 0;
/*
* Make sure to lazy-enable if required to be done explicitly. No need to
* flush any TLB as the VM is not running yet.
*/
if (skeyclass->enable_skeys) {
skeyclass->enable_skeys(ss);
}
while (!ret) {
ram_addr_t addr;
int flags;

View File

@ -803,6 +803,11 @@ DEFINE_CCW_MACHINE(6_2, "6.2", true);
static void ccw_machine_6_1_instance_options(MachineState *machine)
{
ccw_machine_6_2_instance_options(machine);
s390_cpudef_featoff_greater(16, 1, S390_FEAT_NNPA);
s390_cpudef_featoff_greater(16, 1, S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2);
s390_cpudef_featoff_greater(16, 1, S390_FEAT_BEAR_ENH);
s390_cpudef_featoff_greater(16, 1, S390_FEAT_RDP);
s390_cpudef_featoff_greater(16, 1, S390_FEAT_PAI);
}
static void ccw_machine_6_1_class_options(MachineClass *mc)

View File

@ -51,7 +51,7 @@ static bool sccb_verify_boundary(uint64_t sccb_addr, uint16_t sccb_len,
uint32_t code)
{
uint64_t sccb_max_addr = sccb_addr + sccb_len - 1;
uint64_t sccb_boundary = (sccb_addr & PAGE_MASK) + PAGE_SIZE;
uint64_t sccb_boundary = (sccb_addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
switch (code & SCLP_CMD_CODE_MASK) {
case SCLP_CMDW_READ_SCP_INFO:

View File

@ -199,7 +199,7 @@ again:
case 0:
case -ENODEV:
case -EACCES:
return 0;
return ret;
case -EFAULT:
default:
sch_gen_unit_exception(sch);
@ -240,7 +240,7 @@ again:
case -EBUSY:
case -ENODEV:
case -EACCES:
return 0;
return ret;
case -EFAULT:
default:
sch_gen_unit_exception(sch);

View File

@ -146,7 +146,8 @@ struct SubchDev {
static inline void sch_gen_unit_exception(SubchDev *sch)
{
sch->curr_status.scsw.ctrl &= ~SCSW_ACTL_START_PEND;
sch->curr_status.scsw.ctrl &= ~(SCSW_ACTL_DEVICE_ACTIVE |
SCSW_ACTL_SUBCH_ACTIVE);
sch->curr_status.scsw.ctrl |= SCSW_STCTL_PRIMARY |
SCSW_STCTL_SECONDARY |
SCSW_STCTL_ALERT |

View File

@ -81,9 +81,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(S390PCIIOMMU, S390_PCI_IOMMU)
#define ZPCI_SDMA_ADDR 0x100000000ULL
#define ZPCI_EDMA_ADDR 0x1ffffffffffffffULL
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
#define PAGE_DEFAULT_ACC 0
#define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4)
@ -137,7 +134,7 @@ enum ZpciIoatDtype {
#define ZPCI_TABLE_BITS 11
#define ZPCI_PT_BITS 8
#define ZPCI_ST_SHIFT (ZPCI_PT_BITS + PAGE_SHIFT)
#define ZPCI_ST_SHIFT (ZPCI_PT_BITS + TARGET_PAGE_BITS)
#define ZPCI_RT_SHIFT (ZPCI_ST_SHIFT + ZPCI_TABLE_BITS)
#define ZPCI_RTE_FLAG_MASK 0x3fffULL

View File

@ -28,9 +28,72 @@ struct S390SKeysState {
struct S390SKeysClass {
DeviceClass parent_class;
int (*skeys_enabled)(S390SKeysState *ks);
/**
* @skeys_are_enabled:
*
* Check whether storage keys are enabled. If not enabled, they were not
* enabled lazily either by the guest via a storage key instruction or
* by the host during migration.
*
* If disabled, everything not explicitly triggered by the guest,
* such as outgoing migration or dirty/change tracking, should not touch
* storage keys and should not lazily enable it.
*
* @ks: the #S390SKeysState
*
* Returns false if not enabled and true if enabled.
*/
bool (*skeys_are_enabled)(S390SKeysState *ks);
/**
* @enable_skeys:
*
* Lazily enable storage keys. If this function is not implemented,
* setting a storage key will lazily enable storage keys implicitly
* instead. TCG guests have to make sure to flush the TLB of all CPUs
* if storage keys were not enabled before this call.
*
* @ks: the #S390SKeysState
*
* Returns false if not enabled before this call, and true if already
* enabled.
*/
bool (*enable_skeys)(S390SKeysState *ks);
/**
* @get_skeys:
*
* Get storage keys for the given PFN range. This call will fail if
* storage keys have not been lazily enabled yet.
*
* Callers have to validate that a GFN is valid before this call.
*
* @ks: the #S390SKeysState
* @start_gfn: the start GFN to get storage keys for
* @count: the number of storage keys to get
* @keys: the byte array where storage keys will be stored to
*
* Returns 0 on success, returns an error if getting a storage key failed.
*/
int (*get_skeys)(S390SKeysState *ks, uint64_t start_gfn, uint64_t count,
uint8_t *keys);
/**
* @set_skeys:
*
* Set storage keys for the given PFN range. This call will fail if
* storage keys have not been lazily enabled yet and implicit
* enablement is not supported.
*
* Callers have to validate that a GFN is valid before this call.
*
* @ks: the #S390SKeysState
* @start_gfn: the start GFN to set storage keys for
* @count: the number of storage keys to set
* @keys: the byte array where storage keys will be read from
*
* Returns 0 on success, returns an error if setting a storage key failed.
*/
int (*set_skeys)(S390SKeysState *ks, uint64_t start_gfn, uint64_t count,
uint8_t *keys);
};

View File

@ -663,7 +663,13 @@ static uint16_t default_GEN15_GA1[] = {
S390_FEAT_ETOKEN,
};
#define default_GEN16_GA1 EmptyFeat
static uint16_t default_GEN16_GA1[] = {
S390_FEAT_NNPA,
S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2,
S390_FEAT_BEAR_ENH,
S390_FEAT_RDP,
S390_FEAT_PAI,
};
/* QEMU (CPU model) features */

View File

@ -336,9 +336,9 @@ DEF_HELPER_FLAGS_4(stctl, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_FLAGS_4(stctg, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_FLAGS_2(testblock, TCG_CALL_NO_WG, i32, env, i64)
DEF_HELPER_FLAGS_3(tprot, TCG_CALL_NO_WG, i32, env, i64, i64)
DEF_HELPER_FLAGS_2(iske, TCG_CALL_NO_RWG_SE, i64, env, i64)
DEF_HELPER_FLAGS_3(sske, TCG_CALL_NO_RWG, void, env, i64, i64)
DEF_HELPER_FLAGS_2(rrbe, TCG_CALL_NO_RWG, i32, env, i64)
DEF_HELPER_2(iske, i64, env, i64)
DEF_HELPER_3(sske, void, env, i64, i64)
DEF_HELPER_2(rrbe, i32, env, i64)
DEF_HELPER_4(mvcs, i32, env, i64, i64, i64)
DEF_HELPER_4(mvcp, i32, env, i64, i64, i64)
DEF_HELPER_4(sigp, i32, env, i64, i32, i32)

View File

@ -123,7 +123,7 @@ static int ioinst_schib_valid(SCHIB *schib)
}
/* for MB format 1 bits 26-31 of word 11 must be 0 */
/* MBA uses words 10 and 11, it means align on 2**6 */
if ((be16_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_MBFC) &&
if ((be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_MBFC) &&
(be64_to_cpu(schib->mba) & 0x03fUL)) {
return 0;
}

View File

@ -94,6 +94,14 @@ target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr)
return raddr;
}
bool mmu_absolute_addr_valid(target_ulong addr, bool is_write)
{
return address_space_access_valid(&address_space_memory,
addr & TARGET_PAGE_MASK,
TARGET_PAGE_SIZE, is_write,
MEMTXATTRS_UNSPECIFIED);
}
static inline bool read_table_entry(CPUS390XState *env, hwaddr gaddr,
uint64_t *entry)
{
@ -117,7 +125,7 @@ static inline bool read_table_entry(CPUS390XState *env, hwaddr gaddr,
static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
uint64_t asc, uint64_t asce, target_ulong *raddr,
int *flags, int rw)
int *flags)
{
const bool edat1 = (env->cregs[0] & CR0_EDAT) &&
s390_has_feat(S390_FEAT_EDAT);
@ -293,19 +301,26 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags)
{
static S390SKeysClass *skeyclass;
static S390SKeysState *ss;
MachineState *ms = MACHINE(qdev_get_machine());
uint8_t key;
uint8_t key, old_key;
int rc;
if (unlikely(addr >= ms->ram_size)) {
return;
}
/*
* We expect to be called with an absolute address that has already been
* validated, such that we can reliably use it to lookup the storage key.
*/
if (unlikely(!ss)) {
ss = s390_get_skeys_device();
skeyclass = S390_SKEYS_GET_CLASS(ss);
}
/*
* Don't enable storage keys if they are still disabled, i.e., no actual
* storage key instruction was issued yet.
*/
if (!skeyclass->skeys_are_enabled(ss)) {
return;
}
/*
* Whenever we create a new TLB entry, we set the storage key reference
* bit. In case we allow write accesses, we set the storage key change
@ -330,6 +345,7 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags)
trace_get_skeys_nonzero(rc);
return;
}
old_key = key;
switch (rw) {
case MMU_DATA_LOAD:
@ -353,20 +369,23 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags)
/* Any store/fetch sets the reference bit */
key |= SK_R;
rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (rc) {
trace_set_skeys_nonzero(rc);
if (key != old_key) {
rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (rc) {
trace_set_skeys_nonzero(rc);
}
}
}
/**
* Translate a virtual (logical) address into a physical (absolute) address.
* @param vaddr the virtual address
* @param rw 0 = read, 1 = write, 2 = code fetch
* @param rw 0 = read, 1 = write, 2 = code fetch, < 0 = load real address
* @param asc address space control (one of the PSW_ASC_* modes)
* @param raddr the translated address is stored to this pointer
* @param flags the PAGE_READ/WRITE/EXEC flags are stored to this pointer
* @param exc true = inject a program check if a fault occurred
* @param tec the translation exception code if stored to this pointer if
* there is an exception to raise
* @return 0 = success, != 0, the exception to raise
*/
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
@ -420,7 +439,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
}
/* perform the DAT translation */
r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags, rw);
r = mmu_translate_asce(env, vaddr, asc, asce, raddr, flags);
if (unlikely(r)) {
return r;
}
@ -440,10 +459,17 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
}
nodat:
/* Convert real address -> absolute address */
*raddr = mmu_real2abs(env, *raddr);
if (rw >= 0) {
/* Convert real address -> absolute address */
*raddr = mmu_real2abs(env, *raddr);
mmu_handle_skey(*raddr, rw, flags);
if (!mmu_absolute_addr_valid(*raddr, rw == MMU_DATA_STORE)) {
*tec = 0; /* unused */
return PGM_ADDRESSING;
}
mmu_handle_skey(*raddr, rw, flags);
}
return 0;
}
@ -464,12 +490,6 @@ static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages,
if (ret) {
return ret;
}
if (!address_space_access_valid(&address_space_memory, pages[i],
TARGET_PAGE_SIZE, is_write,
MEMTXATTRS_UNSPECIFIED)) {
*tec = 0; /* unused */
return PGM_ADDRESSING;
}
addr += TARGET_PAGE_SIZE;
}
@ -579,6 +599,12 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
*addr = mmu_real2abs(env, raddr & TARGET_PAGE_MASK);
if (!mmu_absolute_addr_valid(*addr, rw == MMU_DATA_STORE)) {
/* unused */
*tec = 0;
return PGM_ADDRESSING;
}
mmu_handle_skey(*addr, rw, flags);
return 0;
}

View File

@ -373,6 +373,9 @@ void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len,
/* mmu_helper.c */
bool mmu_absolute_addr_valid(target_ulong addr, bool is_write);
/* Special access mode only valid for mmu_translate() */
#define MMU_S390_LRA -1
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
target_ulong *raddr, int *flags, uint64_t *tec);
int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,

View File

@ -150,19 +150,6 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
g_assert_not_reached();
}
/* check out of RAM access */
if (!excp &&
!address_space_access_valid(&address_space_memory, raddr,
TARGET_PAGE_SIZE, access_type,
MEMTXATTRS_UNSPECIFIED)) {
MachineState *ms = MACHINE(qdev_get_machine());
qemu_log_mask(CPU_LOG_MMU,
"%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n",
__func__, (uint64_t)raddr, (uint64_t)ms->ram_size);
excp = PGM_ADDRESSING;
tec = 0; /* unused */
}
env->tlb_fill_exc = excp;
env->tlb_fill_tec = tec;

View File

@ -28,6 +28,7 @@
#include "qemu/int128.h"
#include "qemu/atomic128.h"
#include "tcg/tcg.h"
#include "trace.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/s390x/storage-keys.h"
@ -2171,22 +2172,28 @@ uint32_t HELPER(tprot)(CPUS390XState *env, uint64_t a1, uint64_t a2)
/* insert storage key extended */
uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
{
MachineState *ms = MACHINE(qdev_get_machine());
static S390SKeysState *ss;
static S390SKeysClass *skeyclass;
uint64_t addr = wrap_address(env, r2);
uint8_t key;
int rc;
if (addr > ms->ram_size) {
return 0;
addr = mmu_real2abs(env, addr);
if (!mmu_absolute_addr_valid(addr, false)) {
tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC());
}
if (unlikely(!ss)) {
ss = s390_get_skeys_device();
skeyclass = S390_SKEYS_GET_CLASS(ss);
if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) {
tlb_flush_all_cpus_synced(env_cpu(env));
}
}
if (skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key)) {
rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (rc) {
trace_get_skeys_nonzero(rc);
return 0;
}
return key;
@ -2195,23 +2202,30 @@ uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
/* set storage key extended */
void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
{
MachineState *ms = MACHINE(qdev_get_machine());
static S390SKeysState *ss;
static S390SKeysClass *skeyclass;
uint64_t addr = wrap_address(env, r2);
uint8_t key;
int rc;
if (addr > ms->ram_size) {
return;
addr = mmu_real2abs(env, addr);
if (!mmu_absolute_addr_valid(addr, false)) {
tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC());
}
if (unlikely(!ss)) {
ss = s390_get_skeys_device();
skeyclass = S390_SKEYS_GET_CLASS(ss);
if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) {
tlb_flush_all_cpus_synced(env_cpu(env));
}
}
key = (uint8_t) r1;
skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
key = r1 & 0xfe;
rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (rc) {
trace_set_skeys_nonzero(rc);
}
/*
* As we can only flush by virtual address and not all the entries
* that point to a physical address we have to flush the whole TLB.
@ -2222,28 +2236,37 @@ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
/* reset reference bit extended */
uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
{
MachineState *ms = MACHINE(qdev_get_machine());
uint64_t addr = wrap_address(env, r2);
static S390SKeysState *ss;
static S390SKeysClass *skeyclass;
uint8_t re, key;
int rc;
if (r2 > ms->ram_size) {
return 0;
addr = mmu_real2abs(env, addr);
if (!mmu_absolute_addr_valid(addr, false)) {
tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC());
}
if (unlikely(!ss)) {
ss = s390_get_skeys_device();
skeyclass = S390_SKEYS_GET_CLASS(ss);
if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) {
tlb_flush_all_cpus_synced(env_cpu(env));
}
}
if (skeyclass->get_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) {
rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (rc) {
trace_get_skeys_nonzero(rc);
return 0;
}
re = key & (SK_R | SK_C);
key &= ~SK_R;
if (skeyclass->set_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) {
rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
if (rc) {
trace_set_skeys_nonzero(rc);
return 0;
}
/*
@ -2441,7 +2464,7 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, GETPC());
}
exc = mmu_translate(env, addr, 0, asc, &ret, &flags, &tec);
exc = mmu_translate(env, addr, MMU_S390_LRA, asc, &ret, &flags, &tec);
if (exc) {
cc = 3;
ret = exc | 0x80000000;

View File

@ -151,13 +151,26 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num)
/* Set Prefix */
void HELPER(spx)(CPUS390XState *env, uint64_t a1)
{
const uint32_t prefix = a1 & 0x7fffe000;
const uint32_t old_prefix = env->psa;
CPUState *cs = env_cpu(env);
uint32_t prefix = a1 & 0x7fffe000;
if (prefix == old_prefix) {
return;
}
env->psa = prefix;
HELPER_LOG("prefix: %#x\n", prefix);
tlb_flush_page(cs, 0);
tlb_flush_page(cs, TARGET_PAGE_SIZE);
if (prefix != 0) {
tlb_flush_page(cs, prefix);
tlb_flush_page(cs, prefix + TARGET_PAGE_SIZE);
}
if (old_prefix != 0) {
tlb_flush_page(cs, old_prefix);
tlb_flush_page(cs, old_prefix + TARGET_PAGE_SIZE);
}
}
static void update_ckc_timer(CPUS390XState *env)

View File

@ -1,4 +1,5 @@
VPATH+=$(SRC_PATH)/tests/tcg/s390x
S390X_SRC=$(SRC_PATH)/tests/tcg/s390x
VPATH+=$(S390X_SRC)
CFLAGS+=-march=zEC12 -m64
TESTS+=hello-s390x
TESTS+=csst
@ -9,3 +10,17 @@ TESTS+=pack
TESTS+=mvo
TESTS+=mvc
TESTS+=trap
TESTS+=signals-s390x
ifneq ($(HAVE_GDB_BIN),)
GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py
run-gdbstub-signals-s390x: signals-s390x
$(call run-test, $@, $(GDB_SCRIPT) \
--gdb $(HAVE_GDB_BIN) \
--qemu $(QEMU) --qargs "$(QEMU_OPTS)" \
--bin $< --test $(S390X_SRC)/gdbstub/test-signals-s390x.py, \
"mixing signals and debugging on s390x")
EXTRA_RUNS += run-gdbstub-signals-s390x
endif

View File

@ -0,0 +1,76 @@
from __future__ import print_function
#
# Test that signals and debugging mix well together on s390x.
#
# This is launched via tests/guest-debug/run-test.py
#
import gdb
import sys
failcount = 0
def report(cond, msg):
"""Report success/fail of test"""
if cond:
print("PASS: %s" % (msg))
else:
print("FAIL: %s" % (msg))
global failcount
failcount += 1
def run_test():
"""Run through the tests one by one"""
illegal_op = gdb.Breakpoint("illegal_op")
stg = gdb.Breakpoint("stg")
mvc_8 = gdb.Breakpoint("mvc_8")
# Expect the following events:
# 1x illegal_op breakpoint
# 2x stg breakpoint, segv, breakpoint
# 2x mvc_8 breakpoint, segv, breakpoint
for _ in range(14):
gdb.execute("c")
report(illegal_op.hit_count == 1, "illegal_op.hit_count == 1")
report(stg.hit_count == 4, "stg.hit_count == 4")
report(mvc_8.hit_count == 4, "mvc_8.hit_count == 4")
# The test must succeed.
gdb.Breakpoint("_exit")
gdb.execute("c")
status = int(gdb.parse_and_eval("$r2"))
report(status == 0, "status == 0");
#
# This runs as the script it sourced (via -x, via run-test.py)
#
try:
inferior = gdb.selected_inferior()
arch = inferior.architecture()
print("ATTACHED: %s" % arch.name())
except (gdb.error, AttributeError):
print("SKIPPING (not connected)", file=sys.stderr)
exit(0)
if gdb.parse_and_eval("$pc") == 0:
print("SKIP: PC not set")
exit(0)
try:
# These are not very useful in scripts
gdb.execute("set pagination off")
gdb.execute("set confirm off")
# Run the actual tests
run_test()
except (gdb.error):
print("GDB Exception: %s" % (sys.exc_info()[0]))
failcount += 1
pass
print("All tests complete: %d failures" % failcount)
exit(failcount)

View File

@ -0,0 +1,165 @@
#include <assert.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <ucontext.h>
#include <unistd.h>
/*
* Various instructions that generate SIGILL and SIGSEGV. They could have been
* defined in a separate .s file, but this would complicate the build, so the
* inline asm is used instead.
*/
void illegal_op(void);
void after_illegal_op(void);
asm(".globl\tillegal_op\n"
"illegal_op:\t.byte\t0x00,0x00\n"
"\t.globl\tafter_illegal_op\n"
"after_illegal_op:\tbr\t%r14");
void stg(void *dst, unsigned long src);
asm(".globl\tstg\n"
"stg:\tstg\t%r3,0(%r2)\n"
"\tbr\t%r14");
void mvc_8(void *dst, void *src);
asm(".globl\tmvc_8\n"
"mvc_8:\tmvc\t0(8,%r2),0(%r3)\n"
"\tbr\t%r14");
static void safe_puts(const char *s)
{
write(0, s, strlen(s));
write(0, "\n", 1);
}
enum exception {
exception_operation,
exception_translation,
exception_protection,
};
static struct {
int sig;
void *addr;
unsigned long psw_addr;
enum exception exception;
} expected;
static void handle_signal(int sig, siginfo_t *info, void *ucontext)
{
void *page;
int err;
if (sig != expected.sig) {
safe_puts("[ FAILED ] wrong signal");
_exit(1);
}
if (info->si_addr != expected.addr) {
safe_puts("[ FAILED ] wrong si_addr");
_exit(1);
}
if (((ucontext_t *)ucontext)->uc_mcontext.psw.addr != expected.psw_addr) {
safe_puts("[ FAILED ] wrong psw.addr");
_exit(1);
}
switch (expected.exception) {
case exception_translation:
page = mmap(expected.addr, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (page != expected.addr) {
safe_puts("[ FAILED ] mmap() failed");
_exit(1);
}
break;
case exception_protection:
err = mprotect(expected.addr, 4096, PROT_READ | PROT_WRITE);
if (err != 0) {
safe_puts("[ FAILED ] mprotect() failed");
_exit(1);
}
break;
default:
break;
}
}
static void check_sigsegv(void *func, enum exception exception,
unsigned long val)
{
int prot;
unsigned long *page;
unsigned long *addr;
int err;
prot = exception == exception_translation ? PROT_NONE : PROT_READ;
page = mmap(NULL, 4096, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert(page != MAP_FAILED);
if (exception == exception_translation) {
/* Hopefully nothing will be mapped at this address. */
err = munmap(page, 4096);
assert(err == 0);
}
addr = page + (val & 0x1ff);
expected.sig = SIGSEGV;
expected.addr = page;
expected.psw_addr = (unsigned long)func;
expected.exception = exception;
if (func == stg) {
stg(addr, val);
} else {
assert(func == mvc_8);
mvc_8(addr, &val);
}
assert(*addr == val);
err = munmap(page, 4096);
assert(err == 0);
}
int main(void)
{
struct sigaction act;
int err;
memset(&act, 0, sizeof(act));
act.sa_sigaction = handle_signal;
act.sa_flags = SA_SIGINFO;
err = sigaction(SIGILL, &act, NULL);
assert(err == 0);
err = sigaction(SIGSEGV, &act, NULL);
assert(err == 0);
safe_puts("[ RUN ] Operation exception");
expected.sig = SIGILL;
expected.addr = illegal_op;
expected.psw_addr = (unsigned long)after_illegal_op;
expected.exception = exception_operation;
illegal_op();
safe_puts("[ OK ]");
safe_puts("[ RUN ] Translation exception from stg");
check_sigsegv(stg, exception_translation, 42);
safe_puts("[ OK ]");
safe_puts("[ RUN ] Translation exception from mvc");
check_sigsegv(mvc_8, exception_translation, 4242);
safe_puts("[ OK ]");
safe_puts("[ RUN ] Protection exception from stg");
check_sigsegv(stg, exception_protection, 424242);
safe_puts("[ OK ]");
safe_puts("[ RUN ] Protection exception from mvc");
check_sigsegv(mvc_8, exception_protection, 42424242);
safe_puts("[ OK ]");
safe_puts("[ PASSED ]");
_exit(0);
}