提交 2037aad1 编写于 作者: T Thomas Gleixner 提交者: Shile Zhang

x86/mm/cpa: Add debug mechanism

commit 4046460b867f8b041c81c26c09d3bcad6d6e814e upstream.

The whole static protection magic is silently fixing up anything which is
handed in. That's just wrong. The offending call sites need to be fixed.

Add a debug mechanism which emits a warning if a requested mapping needs to be
fixed up. The DETECT debug mechanism is really not meant to be enabled except
for developers, so limit the output hard to the protection fixups.
Signed-off-by: NThomas Gleixner <tglx@linutronix.de>
Reviewed-by: NDave Hansen <dave.hansen@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Bin Yang <bin.yang@intel.com>
Cc: Mark Gross <mark.gross@intel.com>
Link: https://lkml.kernel.org/r/20180917143546.078998733@linutronix.deSigned-off-by: NShile Zhang <shile.zhang@linux.alibaba.com>
Acked-by: NJoseph Qi <joseph.qi@linux.alibaba.com>
上级 28c4f97f
...@@ -42,6 +42,13 @@ struct cpa_data { ...@@ -42,6 +42,13 @@ struct cpa_data {
struct page **pages; struct page **pages;
}; };
enum cpa_warn {
CPA_PROTECT,
CPA_DETECT,
};
static const int cpa_warn_level = CPA_PROTECT;
/* /*
* Serialize cpa() (for !DEBUG_PAGEALLOC which uses large identity mappings) * Serialize cpa() (for !DEBUG_PAGEALLOC which uses large identity mappings)
* using cpa_lock. So that we don't allow any other cpu, with stale large tlb * using cpa_lock. So that we don't allow any other cpu, with stale large tlb
...@@ -395,6 +402,28 @@ static pgprotval_t protect_kernel_text_ro(unsigned long start, ...@@ -395,6 +402,28 @@ static pgprotval_t protect_kernel_text_ro(unsigned long start,
} }
#endif #endif
static inline bool conflicts(pgprot_t prot, pgprotval_t val)
{
return (pgprot_val(prot) & ~val) != pgprot_val(prot);
}
static inline void check_conflict(int warnlvl, pgprot_t prot, pgprotval_t val,
unsigned long start, unsigned long end,
unsigned long pfn, const char *txt)
{
static const char *lvltxt[] = {
[CPA_PROTECT] = "protect",
[CPA_DETECT] = "detect",
};
if (warnlvl > cpa_warn_level || !conflicts(prot, val))
return;
pr_warn("CPA %8s %10s: 0x%016lx - 0x%016lx PFN %lx req %016llx prevent %016llx\n",
lvltxt[warnlvl], txt, start, end, pfn, (unsigned long long)pgprot_val(prot),
(unsigned long long)val);
}
/* /*
* Certain areas of memory on x86 require very specific protection flags, * Certain areas of memory on x86 require very specific protection flags,
* for example the BIOS area or kernel text. Callers don't always get this * for example the BIOS area or kernel text. Callers don't always get this
...@@ -402,19 +431,31 @@ static pgprotval_t protect_kernel_text_ro(unsigned long start, ...@@ -402,19 +431,31 @@ static pgprotval_t protect_kernel_text_ro(unsigned long start,
* checks and fixes these known static required protection bits. * checks and fixes these known static required protection bits.
*/ */
static inline pgprot_t static_protections(pgprot_t prot, unsigned long start, static inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
unsigned long pfn, unsigned long npg) unsigned long pfn, unsigned long npg,
int warnlvl)
{ {
pgprotval_t forbidden; pgprotval_t forbidden, res;
unsigned long end; unsigned long end;
/* Operate on the virtual address */ /* Operate on the virtual address */
end = start + npg * PAGE_SIZE - 1; end = start + npg * PAGE_SIZE - 1;
forbidden = protect_kernel_text(start, end);
forbidden |= protect_kernel_text_ro(start, end); res = protect_kernel_text(start, end);
check_conflict(warnlvl, prot, res, start, end, pfn, "Text NX");
forbidden = res;
res = protect_kernel_text_ro(start, end);
check_conflict(warnlvl, prot, res, start, end, pfn, "Text RO");
forbidden |= res;
/* Check the PFN directly */ /* Check the PFN directly */
forbidden |= protect_pci_bios(pfn, pfn + npg - 1); res = protect_pci_bios(pfn, pfn + npg - 1);
forbidden |= protect_rodata(pfn, pfn + npg - 1); check_conflict(warnlvl, prot, res, start, end, pfn, "PCIBIOS NX");
forbidden |= res;
res = protect_rodata(pfn, pfn + npg - 1);
check_conflict(warnlvl, prot, res, start, end, pfn, "Rodata RO");
forbidden |= res;
return __pgprot(pgprot_val(prot) & ~forbidden); return __pgprot(pgprot_val(prot) & ~forbidden);
} }
...@@ -686,10 +727,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, ...@@ -686,10 +727,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
* in it results in a different pgprot than the first one of the * in it results in a different pgprot than the first one of the
* requested range. If yes, then the page needs to be split. * requested range. If yes, then the page needs to be split.
*/ */
new_prot = static_protections(req_prot, address, pfn, 1); new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT);
pfn = old_pfn; pfn = old_pfn;
for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) {
pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1); pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1,
CPA_DETECT);
if (pgprot_val(chk_prot) != pgprot_val(new_prot)) if (pgprot_val(chk_prot) != pgprot_val(new_prot))
return 1; return 1;
...@@ -1299,7 +1341,8 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) ...@@ -1299,7 +1341,8 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr);
pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); pgprot_val(new_prot) |= pgprot_val(cpa->mask_set);
new_prot = static_protections(new_prot, address, pfn, 1); new_prot = static_protections(new_prot, address, pfn, 1,
CPA_PROTECT);
new_prot = pgprot_clear_protnone_bits(new_prot); new_prot = pgprot_clear_protnone_bits(new_prot);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册