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 | X86_VENDOR_HYGON) )
+ /* 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;