0x7f00
};
-
+void vmx_lazy_load_fpu(struct vcpu *vcpu)
+{
+ if (FP_PSR(vcpu) & IA64_PSR_DFH) {
+ FP_PSR(vcpu) = IA64_PSR_MFH;
+ if (__ia64_per_cpu_var(fp_owner) != vcpu)
+ __ia64_load_fpu(vcpu->arch._thread.fph);
+ }
+}
void vmx_reflect_interruption(u64 ifa, u64 isr, u64 iim,
u64 vec, REGS *regs)
case 25: // IA64_DISABLED_FPREG_VECTOR
if (!(vpsr & IA64_PSR_IC))
goto nested_fault;
- if (FP_PSR(vcpu) & IA64_PSR_DFH) {
- FP_PSR(vcpu) = IA64_PSR_MFH;
- if (__ia64_per_cpu_var(fp_owner) != vcpu)
- __ia64_load_fpu(vcpu->arch._thread.fph);
- }
+ vmx_lazy_load_fpu(vcpu);
if (!(VCPU(vcpu, vpsr) & IA64_PSR_DFH)) {
regs->cr_ipsr &= ~IA64_PSR_DFH;
return;
ia64_fc(imva + 3);
}
+/* xen fpswa call stub. 14 bundles */
+extern const unsigned long xen_ia64_fpswa_call_stub[];
+extern const unsigned long xen_ia64_fpswa_call_stub_end[];
+extern const unsigned long xen_ia64_fpswa_call_stub_patch[];
+asm(
+ ".align 32\n"
+ ".proc xen_ia64_fpswa_call_stub;\n"
+ "xen_ia64_fpswa_call_stub:\n"
+ ".prologue\n"
+ "alloc r3 = ar.pfs, 8, 0, 0, 0\n"
+ ".body\n"
+ "mov r14 = in0\n"
+ "ld8 r15 = [in1], 8\n"
+ ";;\n"
+ "ld8 r16 = [in1]\n"
+ "ld8 r17 = [in2]\n"
+ "ld8 r18 = [in3]\n"
+ "ld8 r19 = [in4]\n"
+ "ld8 r20 = [in5]\n"
+ "ld8 r21 = [in6]\n"
+ "ld8 r22 = [in7], 8\n"
+ ";;\n"
+ "ld8 r23 = [in7], 8\n"
+ ";;\n"
+ "ld8 r24 = [in7], 8\n"
+ ";;\n"
+ "cmp.ne p6, p0 = r24, r0\n"
+ "ld8 r25 = [in7], 8\n"
+ ";;\n"
+ "(p6) tpa r24 = r24\n"
+ "cmp.ne p7, p0 = r25, r0\n"
+ "ld8 r26 = [in7], 8\n"
+ ";;\n"
+ "(p7)tpa r25 = r25\n"
+ "cmp.ne p8, p0 = r26, r0\n"
+ "ld8 r27 = [in7], 8\n"
+ ";;\n"
+ "(p8)tpa r26 = r26\n"
+ "cmp.ne p9, p0 = r27, r0\n"
+ ";;\n"
+ "tpa r27 = r27\n"
+ "xen_ia64_fpswa_call_stub_patch:"
+ "{\n"
+ "mov r2 = " FW_HYPERCALL_FPSWA_STR "\n"
+ "break " __IA64_XEN_HYPERCALL_DEFAULT_STR "\n"
+ "nop.i 0\n"
+ "}\n"
+ "st8 [in2] = r17\n"
+ "st8 [in3] = r18\n"
+ "st8 [in4] = r19\n"
+ "st8 [in5] = r20\n"
+ "st8 [in6] = r21\n"
+ "br.ret.sptk.many rp\n"
+ "xen_ia64_fpswa_call_stub_end:"
+ ".endp xen_ia64_fpswa_call_stub\n"
+);
+
+static void
+build_fpswa_hypercall_bundle(uint64_t *imva, uint64_t brkimm, uint64_t hypnum)
+{
+ INST64_A5 slot0;
+ INST64_I19 slot1;
+ INST64_I18 slot2;
+ IA64_BUNDLE bundle;
+
+ /* slot0: mov r2 = hypnum (low 20 bits) */
+ slot0.inst = 0;
+ slot0.qp = 0;
+ slot0.r1 = 2;
+ slot0.r3 = 0;
+ slot0.major = 0x9;
+
+ slot0.s = 0;
+ slot0.imm9d = hypnum >> 7;
+ slot0.imm5c = hypnum >> 16;
+ slot0.imm7b = hypnum;
+
+ /* slot1: break brkimm */
+ slot1.inst = 0;
+ slot1.qp = 0;
+ slot1.x6 = 0;
+ slot1.x3 = 0;
+ slot1.major = 0x0;
+ slot1.i = brkimm >> 20;
+ slot1.imm20 = brkimm;
+
+ /* slot2: nop.i */
+ slot2.inst = 0;
+ slot2.qp = 0;
+ slot2.imm20 = 0;
+ slot2.y = 0;
+ slot2.x6 = 1;
+ slot2.x3 = 0;
+ slot2.i = 0;
+ slot2.major = 0;
+
+ /* MII bundle */
+ bundle.i64[0] = 0;
+ bundle.i64[1] = 0;
+ bundle.template = 0x0; /* MII */
+ bundle.slot0 = slot0.inst;
+ bundle.slot1a = slot1.inst;
+ bundle.slot1b = slot1.inst >> 18;
+ bundle.slot2 = slot2.inst;
+
+ imva[0] = bundle.i64[0];
+ imva[1] = bundle.i64[1];
+ ia64_fc(imva);
+ ia64_fc(imva + 1);
+}
+
// builds a hypercall bundle at domain physical address
static void
dom_fpswa_hypercall_patch(uint64_t brkimm, unsigned long imva)
unsigned long *entry_imva, *patch_imva;
const unsigned long entry_paddr = FW_HYPERCALL_FPSWA_ENTRY_PADDR;
const unsigned long patch_paddr = FW_HYPERCALL_FPSWA_PATCH_PADDR;
+ const size_t stub_size =
+ (char*)xen_ia64_fpswa_call_stub_end -
+ (char*)xen_ia64_fpswa_call_stub;
+ size_t i;
entry_imva = (unsigned long *)(imva + entry_paddr -
FW_HYPERCALL_BASE_PADDR);
*entry_imva++ = patch_paddr;
*entry_imva = 0;
- build_hypercall_bundle(patch_imva, brkimm, FW_HYPERCALL_FPSWA, 1);
+ /* see dom_fw.h */
+ BUILD_BUG_ON((char*)xen_ia64_fpswa_call_stub_end -
+ (char*)xen_ia64_fpswa_call_stub > 0xff - 16);
+
+ /* call stub */
+ memcpy(patch_imva, xen_ia64_fpswa_call_stub, stub_size);
+ for (i = 0; i < stub_size; i++)
+ ia64_fc(imva + i);
+ patch_imva +=
+ xen_ia64_fpswa_call_stub_patch - xen_ia64_fpswa_call_stub;
+ build_fpswa_hypercall_bundle(patch_imva, brkimm, FW_HYPERCALL_FPSWA);
}
// builds a hypercall bundle at domain physical address
}
}
+void
+ia64_lazy_load_fpu(struct vcpu *v)
+{
+ if (PSCB(v, hpsr_dfh)) {
+ PSCB(v, hpsr_dfh) = 0;
+ PSCB(v, hpsr_mfh) = 1;
+ if (__ia64_per_cpu_var(fp_owner) != v)
+ __ia64_load_fpu(v->arch._thread.fph);
+ }
+}
+
void
ia64_handle_reflection(unsigned long ifa, struct pt_regs *regs,
unsigned long isr, unsigned long iim,
vector = IA64_GENEX_VECTOR;
break;
case 25:
- if (PSCB(v, hpsr_dfh)) {
- PSCB(v, hpsr_dfh) = 0;
- PSCB(v, hpsr_mfh) = 1;
- if (__ia64_per_cpu_var(fp_owner) != v)
- __ia64_load_fpu(v->arch._thread.fph);
- }
+ ia64_lazy_load_fpu(v);
if (!PSCB(v, vpsr_dfh)) {
regs->cr_ipsr &= ~IA64_PSR_DFH;
return;
return;
}
+static int
+fpswa_get_domain_addr(struct vcpu *v, unsigned long gpaddr, size_t size,
+ void **virt, struct page_info **page, const char *name)
+{
+ int cross_page_boundary;
+
+ if (gpaddr == 0) {
+ *virt = 0;
+ return 0;
+ }
+
+ cross_page_boundary = (((gpaddr & ~PAGE_MASK) + size) > PAGE_SIZE);
+ if (unlikely(cross_page_boundary)) {
+ /* this case isn't implemented */
+ gdprintk(XENLOG_ERR,
+ "%s: fpswa hypercall is called with "
+ "page crossing argument %s 0x%lx\n",
+ __func__, name, gpaddr);
+ return -ENOSYS;
+ }
+
+again:
+ *virt = domain_mpa_to_imva(v->domain, gpaddr);
+ *page = virt_to_page(*virt);
+ if (get_page(*page, current->domain) == 0) {
+ if (page_get_owner(*page) != current->domain) {
+ *page = NULL;
+ return -EFAULT;
+ }
+ goto again;
+ }
+
+ return 0;
+}
+
+static fpswa_ret_t
+fw_hypercall_fpswa (struct vcpu *v, struct pt_regs *regs)
+{
+ fpswa_ret_t ret = {-1, 0, 0, 0};
+ unsigned long bundle[2] = { regs->r15, regs->r16};
+ fp_state_t fp_state;
+ struct page_info *lp_page = NULL;
+ struct page_info *lv_page = NULL;
+ struct page_info *hp_page = NULL;
+ struct page_info *hv_page = NULL;
+ XEN_EFI_RR_DECLARE(rr6, rr7);
+
+ if (!fpswa_interface)
+ goto error;
+
+ memset(&fp_state, 0, sizeof(fp_state));
+ fp_state.bitmask_low64 = regs->r22;
+ fp_state.bitmask_high64 = regs->r23;
+
+ /* bit6..bit11 */
+ if ((fp_state.bitmask_low64 & 0xfc0) != 0xfc0) {
+ /* other cases isn't supported yet */
+ gdprintk(XENLOG_ERR, "%s unsupported bitmask_low64 0x%lx\n",
+ __func__, fp_state.bitmask_low64);
+ goto error;
+ }
+ if (regs->r25 == 0)
+ /* fp_state.fp_state_low_volatile must be supplied */
+ goto error;
+
+ /* eager save/lazy restore fpu: f32...f127 */
+ if ((~fp_state.bitmask_low64 & ((1UL << 31) - 1)) != 0 ||
+ ~fp_state.bitmask_high64 != 0) {
+ if (VMX_DOMAIN(v))
+ vmx_lazy_load_fpu(v);
+ else
+ ia64_lazy_load_fpu(v);
+ }
+
+ if (fpswa_get_domain_addr(v, regs->r24,
+ sizeof(fp_state.fp_state_low_preserved),
+ (void*)&fp_state.fp_state_low_preserved,
+ &lp_page, "fp_state_low_preserved") < 0)
+ goto error;
+ if (fpswa_get_domain_addr(v, regs->r25,
+ sizeof(fp_state.fp_state_low_volatile),
+ (void*)&fp_state.fp_state_low_volatile,
+ &lv_page, "fp_state_low_volatile") < 0)
+ goto error;
+ if (fpswa_get_domain_addr(v, regs->r26,
+ sizeof(fp_state.fp_state_high_preserved),
+ (void*)&fp_state.fp_state_high_preserved,
+ &hp_page, "fp_state_low_preserved") < 0)
+ goto error;
+ if (fpswa_get_domain_addr(v, regs->r27,
+ sizeof(fp_state.fp_state_high_volatile),
+ (void*)&fp_state.fp_state_high_volatile,
+ &hv_page, "fp_state_high_volatile") < 0)
+ goto error;
+
+ XEN_EFI_RR_ENTER(rr6, rr7);
+ ret = (*fpswa_interface->fpswa)(regs->r14,
+ bundle,
+ ®s->r17, /* pipsr */
+ ®s->r18, /* pfsr */
+ ®s->r19, /* pisr */
+ ®s->r20, /* ppreds */
+ ®s->r21, /* pifs */
+ &fp_state);
+ XEN_EFI_RR_LEAVE(rr6, rr7);
+
+error:
+ if (lp_page != NULL)
+ put_page(lp_page);
+ if (lv_page != NULL)
+ put_page(lv_page);
+ if (hp_page != NULL)
+ put_page(hp_page);
+ if (hv_page != NULL)
+ put_page(hv_page);
+ return ret;
+}
+
static fpswa_ret_t
-fw_hypercall_fpswa (struct vcpu *v)
+fw_hypercall_fpswa_error(void)
{
- return PSCBX(v, fpswa_ret);
+ return (fpswa_ret_t) {-1, 0, 0, 0};
}
IA64FAULT
case FW_HYPERCALL_SET_SHARED_INFO_VA:
regs->r8 = domain_set_shared_info_va (regs->r28);
break;
- case FW_HYPERCALL_FPSWA:
- fpswa_ret = fw_hypercall_fpswa (v);
+ case FW_HYPERCALL_FPSWA_BASE:
+ switch (regs->r2) {
+ case FW_HYPERCALL_FPSWA_BROKEN:
+ gdprintk(XENLOG_WARNING,
+ "Old fpswa hypercall was called (0x%lx).\n"
+ "Please update your domain builder. ip 0x%lx\n",
+ FW_HYPERCALL_FPSWA_BROKEN, regs->cr_iip);
+ fpswa_ret = fw_hypercall_fpswa_error();
+ break;
+ case FW_HYPERCALL_FPSWA:
+ fpswa_ret = fw_hypercall_fpswa(v, regs);
+ break;
+ default:
+ gdprintk(XENLOG_ERR, "unknown fpswa hypercall %lx\n",
+ regs->r2);
+ fpswa_ret = fw_hypercall_fpswa_error();
+ break;
+ }
regs->r8 = fpswa_ret.status;
regs->r9 = fpswa_ret.err0;
regs->r10 = fpswa_ret.err1;
struct { unsigned long qp:6, imm20:20, :1, x6:6, :3, i:1, major:4; };
} INST64_B9;
+typedef union U_INST64_I18 {
+ IA64_INST inst;
+ struct { unsigned long qp:6, imm20:20, y:1, x6:6, x3:3, i:1, major:4; };
+} INST64_I18;
+
typedef union U_INST64_I19 {
IA64_INST inst;
struct { unsigned long qp:6, imm20:20, :1, x6:6, x3:3, i:1, major:4; };
INST64_B4 B4; // used in build_hypercall_bundle only
INST64_B8 B8; // rfi, bsw.[01]
INST64_B9 B9; // break.b
+ INST64_I18 I18; // nop.i used in build_fpswa_hypercall_bundle only
INST64_I19 I19; // used in build_hypercall_bundle only
INST64_I26 I26; // mov register to ar (I unit)
INST64_I27 I27; // mov immediate to ar (I unit)
/*
* This is a hypercall number for FPSWA.
- * FPSWA hypercall uses 2 bundles for a pseudo-entry-point and a hypercall-patch.
+ * FPSWA hypercall uses one bundle for a pseudo-entry-point
+ * and 14 bundles for a hypercall-patch.
+ *
+ * 0x500 was used before. But that implemetation is broken.
+ * To keep hypercall abi, 0x500 is obsoleted and allocate 0x501 for
+ * fspwa hypercall.
*/
#define FW_HYPERCALL_FPSWA_ENTRY_INDEX 0x90UL
#define FW_HYPERCALL_FPSWA_PATCH_INDEX 0x91UL
#define FW_HYPERCALL_FPSWA_ENTRY_PADDR FW_HYPERCALL_PADDR(FW_HYPERCALL_FPSWA_ENTRY_INDEX)
#define FW_HYPERCALL_FPSWA_PATCH_PADDR FW_HYPERCALL_PADDR(FW_HYPERCALL_FPSWA_PATCH_INDEX)
-#define FW_HYPERCALL_FPSWA 0x500UL
-#define FW_HYPERCALL_FPSWA_STR "0x500"
+#define FW_HYPERCALL_FPSWA_BASE 0x500UL
+#define FW_HYPERCALL_FPSWA_BROKEN 0x500UL
+#define FW_HYPERCALL_FPSWA 0x501UL
+#define FW_HYPERCALL_FPSWA_STR "0x501"
/* Set the shared_info base virtual address. */
#define FW_HYPERCALL_SET_SHARED_INFO_VA 0x600UL
unsigned long iim, unsigned long itir, unsigned long arg5,
unsigned long arg6, unsigned long arg7, unsigned long stack);
+void
+ia64_lazy_load_fpu(struct vcpu *vcpu);
+
#endif /* __ASM_DOMAIN_H__ */
/*
extern void vmx_send_assist_req(struct vcpu *v);
extern void deliver_pal_init(struct vcpu *vcpu);
extern void vmx_pend_pal_init(struct domain *d);
+extern void vmx_lazy_load_fpu(struct vcpu *vcpu);
static inline vcpu_iodata_t *get_vio(struct vcpu *v)
{