|
@@ -1420,6 +1420,29 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Machine check recovery code needs to change cache mode of poisoned
|
|
|
+ * pages to UC to avoid speculative access logging another error. But
|
|
|
+ * passing the address of the 1:1 mapping to set_memory_uc() is a fine
|
|
|
+ * way to encourage a speculative access. So we cheat and flip the top
|
|
|
+ * bit of the address. This works fine for the code that updates the
|
|
|
+ * page tables. But at the end of the process we need to flush the cache
|
|
|
+ * and the non-canonical address causes a #GP fault when used by the
|
|
|
+ * CLFLUSH instruction.
|
|
|
+ *
|
|
|
+ * But in the common case we already have a canonical address. This code
|
|
|
+ * will fix the top bit if needed and is a no-op otherwise.
|
|
|
+ */
|
|
|
+static inline unsigned long make_addr_canonical_again(unsigned long addr)
|
|
|
+{
|
|
|
+#ifdef CONFIG_X86_64
|
|
|
+ return (long)(addr << 1) >> 1;
|
|
|
+#else
|
|
|
+ return addr;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int change_page_attr_set_clr(unsigned long *addr, int numpages,
|
|
|
pgprot_t mask_set, pgprot_t mask_clr,
|
|
|
int force_split, int in_flag,
|
|
@@ -1465,7 +1488,7 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
|
|
|
* Save address for cache flush. *addr is modified in the call
|
|
|
* to __change_page_attr_set_clr() below.
|
|
|
*/
|
|
|
- baddr = *addr;
|
|
|
+ baddr = make_addr_canonical_again(*addr);
|
|
|
}
|
|
|
|
|
|
/* Must avoid aliasing mappings in the highmem code */
|