#define KVM_REG_MIPS_CP0_SEGCTL0       MIPS_CP0_64(5, 2)
 #define KVM_REG_MIPS_CP0_SEGCTL1       MIPS_CP0_64(5, 3)
 #define KVM_REG_MIPS_CP0_SEGCTL2       MIPS_CP0_64(5, 4)
+#define KVM_REG_MIPS_CP0_PWBASE                MIPS_CP0_64(5, 5)
+#define KVM_REG_MIPS_CP0_PWFIELD       MIPS_CP0_64(5, 6)
+#define KVM_REG_MIPS_CP0_PWSIZE                MIPS_CP0_64(5, 7)
 #define KVM_REG_MIPS_CP0_WIRED         MIPS_CP0_32(6, 0)
+#define KVM_REG_MIPS_CP0_PWCTL         MIPS_CP0_32(6, 6)
 #define KVM_REG_MIPS_CP0_HWRENA                MIPS_CP0_32(7, 0)
 #define KVM_REG_MIPS_CP0_BADVADDR      MIPS_CP0_64(8, 0)
 #define KVM_REG_MIPS_CP0_BADINSTR      MIPS_CP0_32(8, 1)
 __BUILD_KVM_RW_HW(segctl0,        l,  MIPS_CP0_TLB_PG_MASK,  2)
 __BUILD_KVM_RW_HW(segctl1,        l,  MIPS_CP0_TLB_PG_MASK,  3)
 __BUILD_KVM_RW_HW(segctl2,        l,  MIPS_CP0_TLB_PG_MASK,  4)
+__BUILD_KVM_RW_HW(pwbase,         l,  MIPS_CP0_TLB_PG_MASK,  5)
+__BUILD_KVM_RW_HW(pwfield,        l,  MIPS_CP0_TLB_PG_MASK,  6)
+__BUILD_KVM_RW_HW(pwsize,         l,  MIPS_CP0_TLB_PG_MASK,  7)
 __BUILD_KVM_RW_HW(wired,          32, MIPS_CP0_TLB_WIRED,    0)
+__BUILD_KVM_RW_HW(pwctl,          32, MIPS_CP0_TLB_WIRED,    6)
 __BUILD_KVM_RW_HW(hwrena,         32, MIPS_CP0_HWRENA,       0)
 __BUILD_KVM_RW_HW(badvaddr,       l,  MIPS_CP0_BAD_VADDR,    0)
 __BUILD_KVM_RW_HW(badinstr,       32, MIPS_CP0_BAD_VADDR,    1)
 
        KVM_REG_MIPS_CP0_SEGCTL2,
 };
 
+static u64 kvm_vz_get_one_regs_htw[] = {
+       KVM_REG_MIPS_CP0_PWBASE,
+       KVM_REG_MIPS_CP0_PWFIELD,
+       KVM_REG_MIPS_CP0_PWSIZE,
+       KVM_REG_MIPS_CP0_PWCTL,
+};
+
 static u64 kvm_vz_get_one_regs_kscratch[] = {
        KVM_REG_MIPS_CP0_KSCRATCH1,
        KVM_REG_MIPS_CP0_KSCRATCH2,
                ret += ARRAY_SIZE(kvm_vz_get_one_regs_contextconfig);
        if (cpu_guest_has_segments)
                ret += ARRAY_SIZE(kvm_vz_get_one_regs_segments);
+       if (cpu_guest_has_htw)
+               ret += ARRAY_SIZE(kvm_vz_get_one_regs_htw);
        ret += __arch_hweight8(cpu_data[0].guest.kscratch_mask);
 
        return ret;
                        return -EFAULT;
                indices += ARRAY_SIZE(kvm_vz_get_one_regs_segments);
        }
+       if (cpu_guest_has_htw) {
+               if (copy_to_user(indices, kvm_vz_get_one_regs_htw,
+                                sizeof(kvm_vz_get_one_regs_htw)))
+                       return -EFAULT;
+               indices += ARRAY_SIZE(kvm_vz_get_one_regs_htw);
+       }
        for (i = 0; i < 6; ++i) {
                if (!cpu_guest_has_kscr(i + 2))
                        continue;
                        return -EINVAL;
                *v = read_gc0_segctl2();
                break;
+       case KVM_REG_MIPS_CP0_PWBASE:
+               if (!cpu_guest_has_htw)
+                       return -EINVAL;
+               *v = read_gc0_pwbase();
+               break;
+       case KVM_REG_MIPS_CP0_PWFIELD:
+               if (!cpu_guest_has_htw)
+                       return -EINVAL;
+               *v = read_gc0_pwfield();
+               break;
+       case KVM_REG_MIPS_CP0_PWSIZE:
+               if (!cpu_guest_has_htw)
+                       return -EINVAL;
+               *v = read_gc0_pwsize();
+               break;
        case KVM_REG_MIPS_CP0_WIRED:
                *v = (long)read_gc0_wired();
                break;
+       case KVM_REG_MIPS_CP0_PWCTL:
+               if (!cpu_guest_has_htw)
+                       return -EINVAL;
+               *v = read_gc0_pwctl();
+               break;
        case KVM_REG_MIPS_CP0_HWRENA:
                *v = (long)read_gc0_hwrena();
                break;
                        return -EINVAL;
                write_gc0_segctl2(v);
                break;
+       case KVM_REG_MIPS_CP0_PWBASE:
+               if (!cpu_guest_has_htw)
+                       return -EINVAL;
+               write_gc0_pwbase(v);
+               break;
+       case KVM_REG_MIPS_CP0_PWFIELD:
+               if (!cpu_guest_has_htw)
+                       return -EINVAL;
+               write_gc0_pwfield(v);
+               break;
+       case KVM_REG_MIPS_CP0_PWSIZE:
+               if (!cpu_guest_has_htw)
+                       return -EINVAL;
+               write_gc0_pwsize(v);
+               break;
        case KVM_REG_MIPS_CP0_WIRED:
                change_gc0_wired(MIPSR6_WIRED_WIRED, v);
                break;
+       case KVM_REG_MIPS_CP0_PWCTL:
+               if (!cpu_guest_has_htw)
+                       return -EINVAL;
+               write_gc0_pwctl(v);
+               break;
        case KVM_REG_MIPS_CP0_HWRENA:
                write_gc0_hwrena(v);
                break;
                kvm_restore_gc0_segctl2(cop0);
        }
 
+       /* restore HTW registers */
+       if (cpu_guest_has_htw) {
+               kvm_restore_gc0_pwbase(cop0);
+               kvm_restore_gc0_pwfield(cop0);
+               kvm_restore_gc0_pwsize(cop0);
+               kvm_restore_gc0_pwctl(cop0);
+       }
+
        /* restore Root.GuestCtl2 from unused Guest guestctl2 register */
        if (cpu_has_guestctl2)
                write_c0_guestctl2(
                kvm_save_gc0_segctl2(cop0);
        }
 
+       /* save HTW registers if enabled in guest */
+       if (cpu_guest_has_htw &&
+           kvm_read_sw_gc0_config3(cop0) & MIPS_CONF3_PW) {
+               kvm_save_gc0_pwbase(cop0);
+               kvm_save_gc0_pwfield(cop0);
+               kvm_save_gc0_pwsize(cop0);
+               kvm_save_gc0_pwctl(cop0);
+       }
+
        kvm_vz_save_timer(vcpu);
 
        /* save Root.GuestCtl2 in unused Guest guestctl2 register */
                kvm_write_sw_gc0_segctl2(cop0, 0x00380438);
        }
 
+       /* reset HTW registers */
+       if (cpu_guest_has_htw && cpu_has_mips_r6) {
+               /* PWField */
+               kvm_write_sw_gc0_pwfield(cop0, 0x0c30c302);
+               /* PWSize */
+               kvm_write_sw_gc0_pwsize(cop0, 1 << MIPS_PWSIZE_PTW_SHIFT);
+       }
+
        /* start with no pending virtual guest interrupts */
        if (cpu_has_guestctl2)
                cop0->reg[MIPS_CP0_GUESTCTL2][MIPS_CP0_GUESTCTL2_SEL] = 0;