* 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:
commit
abf7aee72e
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue