#include <xen/livepatch.h>
#include <xen/sched.h>
#include <xen/vm_event.h>
+#include <xen/virtual_region.h>
#include <asm/fixmap.h>
#include <asm/nmi.h>
return -EBUSY;
}
-int arch_livepatch_quiesce(void)
+int noinline arch_livepatch_quiesce(void)
{
+ /* If Shadow Stacks are in use, disable CR4.CET so we can modify CR0.WP. */
+ if ( cpu_has_xen_shstk )
+ write_cr4(read_cr4() & ~X86_CR4_CET);
+
/* Disable WP to allow changes to read-only pages. */
write_cr0(read_cr0() & ~X86_CR0_WP);
return 0;
}
-void arch_livepatch_revive(void)
+void noinline arch_livepatch_revive(void)
{
/* Reinstate WP. */
write_cr0(read_cr0() | X86_CR0_WP);
+
+ /* Clobber dirty bits and reinstate CET, if applicable. */
+ if ( IS_ENABLED(CONFIG_XEN_SHSTK) && cpu_has_xen_shstk )
+ {
+ unsigned long tmp;
+
+ reset_virtual_region_perms();
+
+ write_cr4(read_cr4() | X86_CR4_CET);
+
+ /*
+ * Fix up the return address on the shadow stack, which currently
+ * points at arch_livepatch_quiesce()'s caller.
+ *
+ * Note: this is somewhat fragile, and depends on both
+ * arch_livepatch_{quiesce,revive}() being called from the same
+ * function, which is currently the case.
+ *
+ * Any error will result in Xen dying with #CP, and its too late to
+ * recover in any way.
+ */
+ asm volatile ("rdsspq %[ssp];"
+ "wrssq %[addr], (%[ssp]);"
+ : [ssp] "=&r" (tmp)
+ : [addr] "r" (__builtin_return_address(0)));
+ }
}
int arch_livepatch_verify_func(const struct livepatch_func *func)
#include <xen/init.h>
#include <xen/kernel.h>
+#include <xen/mm.h>
#include <xen/rcupdate.h>
#include <xen/spinlock.h>
#include <xen/virtual_region.h>
remove_virtual_region(r);
}
+#if defined(CONFIG_LIVEPATCH) && defined(CONFIG_XEN_SHSTK)
+void reset_virtual_region_perms(void)
+{
+ const struct virtual_region *region;
+
+ rcu_read_lock(&rcu_virtual_region_lock);
+ list_for_each_entry_rcu( region, &virtual_region_list, list )
+ modify_xen_mappings((unsigned long)region->start,
+ ROUNDUP((unsigned long)region->end, PAGE_SIZE),
+ PAGE_HYPERVISOR_RX);
+ rcu_read_unlock(&rcu_virtual_region_lock);
+}
+#endif
+
void __init unregister_init_virtual_region(void)
{
BUG_ON(system_state != SYS_STATE_active);