aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target-s390x/cpu.h11
-rw-r--r--target-s390x/mmu_helper.c102
2 files changed, 113 insertions, 0 deletions
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index f8cea308b6..a9ab587627 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -287,6 +287,7 @@ typedef struct CPUS390XState {
#define FLAG_MASK_32 0x00001000
/* Control register 0 bits */
+#define CR0_LOWPROT 0x0000000010000000ULL
#define CR0_EDAT 0x0000000000800000ULL
static inline int cpu_mmu_index (CPUS390XState *env)
@@ -897,6 +898,16 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code);
uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
uint64_t vr);
+int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, void *hostbuf, int len,
+ bool is_write);
+
+#define s390_cpu_virt_mem_read(cpu, laddr, dest, len) \
+ s390_cpu_virt_mem_rw(cpu, laddr, dest, len, false)
+#define s390_cpu_virt_mem_write(cpu, laddr, dest, len) \
+ s390_cpu_virt_mem_rw(cpu, laddr, dest, len, true)
+#define s390_cpu_virt_mem_check_write(cpu, laddr, len) \
+ s390_cpu_virt_mem_rw(cpu, laddr, NULL, len, true)
+
/* The value of the TOD clock for 1.1.1970. */
#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL
diff --git a/target-s390x/mmu_helper.c b/target-s390x/mmu_helper.c
index a1ee992f75..b061c85aff 100644
--- a/target-s390x/mmu_helper.c
+++ b/target-s390x/mmu_helper.c
@@ -15,6 +15,9 @@
* GNU General Public License for more details.
*/
+#include "qemu/error-report.h"
+#include "exec/address-spaces.h"
+#include "sysemu/kvm.h"
#include "cpu.h"
/* #define DEBUG_S390 */
@@ -368,3 +371,102 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
return r;
}
+
+/**
+ * lowprot_enabled: Check whether low-address protection is enabled
+ */
+static bool lowprot_enabled(const CPUS390XState *env)
+{
+ if (!(env->cregs[0] & CR0_LOWPROT)) {
+ return false;
+ }
+ if (!(env->psw.mask & PSW_MASK_DAT)) {
+ return true;
+ }
+
+ /* Check the private-space control bit */
+ switch (env->psw.mask & PSW_MASK_ASC) {
+ case PSW_ASC_PRIMARY:
+ return !(env->cregs[1] & _ASCE_PRIVATE_SPACE);
+ case PSW_ASC_SECONDARY:
+ return !(env->cregs[7] & _ASCE_PRIVATE_SPACE);
+ case PSW_ASC_HOME:
+ return !(env->cregs[13] & _ASCE_PRIVATE_SPACE);
+ default:
+ /* We don't support access register mode */
+ error_report("unsupported addressing mode");
+ exit(1);
+ }
+}
+
+/**
+ * translate_pages: Translate a set of consecutive logical page addresses
+ * to absolute addresses
+ */
+static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages,
+ target_ulong *pages, bool is_write)
+{
+ bool lowprot = is_write && lowprot_enabled(&cpu->env);
+ uint64_t asc = cpu->env.psw.mask & PSW_MASK_ASC;
+ CPUS390XState *env = &cpu->env;
+ int ret, i, pflags;
+
+ for (i = 0; i < nr_pages; i++) {
+ /* Low-address protection? */
+ if (lowprot && (addr < 512 || (addr >= 4096 && addr < 4096 + 512))) {
+ trigger_access_exception(env, PGM_PROTECTION, ILEN_LATER_INC, 0);
+ return -EACCES;
+ }
+ ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, true);
+ if (ret) {
+ return ret;
+ }
+ if (!address_space_access_valid(&address_space_memory, pages[i],
+ TARGET_PAGE_SIZE, is_write)) {
+ program_interrupt(env, PGM_ADDRESSING, 0);
+ return -EFAULT;
+ }
+ addr += TARGET_PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+/**
+ * s390_cpu_virt_mem_rw:
+ * @laddr: the logical start address
+ * @hostbuf: buffer in host memory. NULL = do only checks w/o copying
+ * @len: length that should be transfered
+ * @is_write: true = write, false = read
+ * Returns: 0 on success, non-zero if an exception occured
+ *
+ * Copy from/to guest memory using logical addresses. Note that we inject a
+ * program interrupt in case there is an error while accessing the memory.
+ */
+int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, void *hostbuf,
+ int len, bool is_write)
+{
+ int currlen, nr_pages, i;
+ target_ulong *pages;
+ int ret;
+
+ nr_pages = (((laddr & ~TARGET_PAGE_MASK) + len - 1) >> TARGET_PAGE_BITS)
+ + 1;
+ pages = g_malloc(nr_pages * sizeof(*pages));
+
+ ret = translate_pages(cpu, laddr, nr_pages, pages, is_write);
+ if (ret == 0 && hostbuf != NULL) {
+ /* Copy data by stepping through the area page by page */
+ for (i = 0; i < nr_pages; i++) {
+ currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE));
+ cpu_physical_memory_rw(pages[i] | (laddr & ~TARGET_PAGE_MASK),
+ hostbuf, currlen, is_write);
+ laddr += currlen;
+ hostbuf += currlen;
+ len -= currlen;
+ }
+ }
+
+ g_free(pages);
+ return ret;
+}