diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c index cda9fbba00..d6e3f4dfe7 100644 --- a/xen/arch/x86/x86_64/mm.c +++ b/xen/arch/x86/x86_64/mm.c @@ -1054,17 +1054,53 @@ long do_set_segment_base(unsigned int which, unsigned long base) break; case SEGBASE_GS_USER_SEL: - __asm__ __volatile__ ( - " swapgs \n" - "1: movl %k0,%%gs \n" - " "safe_swapgs" \n" - ".section .fixup,\"ax\" \n" - "2: xorl %k0,%k0 \n" - " jmp 1b \n" - ".previous \n" - _ASM_EXTABLE(1b, 2b) - : "+r" (base) ); + { + unsigned int sel = (uint16_t)base; + + /* + * We wish to update the user %gs from the GDT/LDT. Currently, the + * guest kernel's GS_BASE is in context. + */ + asm volatile ( "swapgs" ); + + if ( sel > 3 ) + /* Fix up RPL for non-NUL selectors. */ + sel |= 3; + else if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) + /* Work around NUL segment behaviour on AMD hardware. */ + asm volatile ( "mov %[sel], %%gs" + :: [sel] "r" (FLAT_USER_DS32) ); + + /* + * Load the chosen selector, with fault handling. + * + * Errors ought to fail the hypercall, but that was never built in + * originally, and Linux will BUG() if this call fails. + * + * NUL the selector in the case of an error. This too needs to deal + * with the AMD NUL segment behaviour, but it is already a slowpath in + * #GP context so perform the flat load unconditionally to avoid + * complicated logic. + * + * Anyone wanting to check for errors from this hypercall should + * re-read %gs and compare against the input. + */ + asm volatile ( "1: mov %[sel], %%gs\n\t" + ".section .fixup, \"ax\", @progbits\n\t" + "2: mov %k[flat], %%gs\n\t" + " xor %[sel], %[sel]\n\t" + " jmp 1b\n\t" + ".previous\n\t" + _ASM_EXTABLE(1b, 2b) + : [sel] "+r" (sel) + : [flat] "r" (FLAT_USER_DS32) ); + + /* Update the cache of the inactive base, as read from the GDT/LDT. */ + v->arch.pv.gs_base_user = rdgsbase(); + + asm volatile ( safe_swapgs ); break; + } default: ret = -EINVAL;