diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 38017a3..dd37962 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -113,6 +113,13 @@ void gen_a64_set_pc_im(uint64_t val)
     tcg_gen_movi_i64(cpu_pc, val);
 }
 
+/* Force a TB lookup after an instruction that changes the CPU state */
+static inline void gen_lookup_tb(DisasContext *s)
+{
+    gen_a64_set_pc_im(s->pc);
+    s->is_jmp = DISAS_UPDATE;
+}
+
 static void gen_exception(int excp)
 {
     TCGv_i32 tmp = tcg_temp_new_i32();
@@ -731,28 +738,88 @@ static void handle_msr_i(DisasContext *s, uint32_t insn,
     unsupported_encoding(s, insn);
 }
 
-/* C5.6.204 SYS */
-static void handle_sys(DisasContext *s, uint32_t insn, unsigned int l,
-                       unsigned int op1, unsigned int op2,
+/* C5.6.129 MRS - move from system register
+ * C5.6.131 MSR (register) - move to system register
+ * C5.6.204 SYS
+ * C5.6.205 SYSL
+ * These are all essentially the same insn in 'read' and 'write'
+ * versions, with varying op0 fields.
+ */
+static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
+                       unsigned int op0, unsigned int op1, unsigned int op2,
                        unsigned int crn, unsigned int crm, unsigned int rt)
 {
-    unsupported_encoding(s, insn);
-}
+    const ARMCPRegInfo *ri;
+    TCGv_i64 tcg_rt;
 
-/* C5.6.129 MRS - move from system register */
-static void handle_mrs(DisasContext *s, uint32_t insn, unsigned int op0,
-                       unsigned int op1, unsigned int op2,
-                       unsigned int crn, unsigned int crm, unsigned int rt)
-{
-    unsupported_encoding(s, insn);
-}
+    ri = get_arm_cp_reginfo(s->cp_regs,
+                            ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP,
+                                               crn, crm, op0, op1, op2));
 
-/* C5.6.131 MSR (register) - move to system register */
-static void handle_msr(DisasContext *s, uint32_t insn, unsigned int op0,
-                       unsigned int op1, unsigned int op2,
-                       unsigned int crn, unsigned int crm, unsigned int rt)
-{
-    unsupported_encoding(s, insn);
+    if (!ri) {
+        /* Unknown register */
+        unallocated_encoding(s);
+        return;
+    }
+
+    /* Check access permissions */
+    if (!cp_access_ok(env, ri, isread)) {
+        unallocated_encoding(s);
+        return;
+    }
+
+    /* Handle special cases first */
+    switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) {
+    case ARM_CP_NOP:
+        return;
+    default:
+        break;
+    }
+
+    if (use_icount && (ri->type & ARM_CP_IO)) {
+        gen_io_start();
+    }
+
+    tcg_rt = cpu_reg(s, rt);
+
+    if (isread) {
+        if (ri->type & ARM_CP_CONST) {
+            tcg_gen_movi_i64(tcg_rt, ri->resetvalue);
+        } else if (ri->readfn) {
+            TCGv_ptr tmpptr;
+            gen_a64_set_pc_im(s->pc - 4);
+            tmpptr = tcg_const_ptr(ri);
+            gen_helper_get_cp_reg64(tcg_rt, cpu_env, tmpptr);
+            tcg_temp_free_ptr(tmpptr);
+        } else {
+            tcg_gen_ld_i64(tcg_rt, cpu_env, ri->fieldoffset);
+        }
+    } else {
+        if (ri->type & ARM_CP_CONST) {
+            /* If not forbidden by access permissions, treat as WI */
+            return;
+        } else if (ri->writefn) {
+            TCGv_ptr tmpptr;
+            gen_a64_set_pc_im(s->pc - 4);
+            tmpptr = tcg_const_ptr(ri);
+            gen_helper_set_cp_reg64(cpu_env, tmpptr, tcg_rt);
+            tcg_temp_free_ptr(tmpptr);
+        } else {
+            tcg_gen_st_i64(tcg_rt, cpu_env, ri->fieldoffset);
+        }
+    }
+
+    if (use_icount && (ri->type & ARM_CP_IO)) {
+        /* I/O operations must end the TB here (whether read or write) */
+        gen_io_end();
+        gen_lookup_tb(s);
+    } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
+        /* We default to ending the TB on a coprocessor register write,
+         * but allow this to be suppressed by the register definition
+         * (usually only necessary to work around guest bugs).
+         */
+        gen_lookup_tb(s);
+    }
 }
 
 /* C3.2.4 System
@@ -793,17 +860,7 @@ static void disas_system(DisasContext *s, uint32_t insn)
         }
         return;
     }
-
-    if (op0 == 1) {
-        /* C5.6.204 SYS */
-        handle_sys(s, insn, l, op1, op2, crn, crm, rt);
-    } else if (l) { /* op0 > 1 */
-        /* C5.6.129 MRS - move from system register */
-        handle_mrs(s, insn, op0, op1, op2, crn, crm, rt);
-    } else {
-        /* C5.6.131 MSR (register) - move to system register */
-        handle_msr(s, insn, op0, op1, op2, crn, crm, rt);
-    }
+    handle_sys(s, insn, l, op0, op1, op2, crn, crm, rt);
 }
 
 /* C3.2.3 Exception generation
