提交 9bb4606c 编写于 作者: T Thomas Gleixner 提交者: Shile Zhang

x86/mm/cpa: Rework static_protections()

commit afd7969a99e072e6aa0d88511176d4d2f3009fd9 upstream.

static_protections() is pretty unreadable. Split it up into separate checks
for each protection area.
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/20180917143545.913005317@linutronix.deSigned-off-by: NShile Zhang <shile.zhang@linux.alibaba.com>
Acked-by: NJoseph Qi <joseph.qi@linux.alibaba.com>
上级 395a831b
...@@ -286,84 +286,118 @@ static void cpa_flush_array(unsigned long *start, int numpages, int cache, ...@@ -286,84 +286,118 @@ static void cpa_flush_array(unsigned long *start, int numpages, int cache,
} }
} }
#ifdef CONFIG_PCI_BIOS
/* /*
* Certain areas of memory on x86 require very specific protection flags, * The BIOS area between 640k and 1Mb needs to be executable for PCI BIOS
* for example the BIOS area or kernel text. Callers don't always get this * based config access (CONFIG_PCI_GOBIOS) support.
* right (again, ioremap() on BIOS memory is not uncommon) so this function
* checks and fixes these known static required protection bits.
*/ */
static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, #define BIOS_PFN PFN_DOWN(BIOS_BEGIN)
unsigned long pfn) #define BIOS_PFN_END PFN_DOWN(BIOS_END)
{
pgprot_t forbidden = __pgprot(0);
/* static pgprotval_t protect_pci_bios(unsigned long pfn)
* The BIOS area between 640k and 1Mb needs to be executable for {
* PCI BIOS based config access (CONFIG_PCI_GOBIOS) support. if (pcibios_enabled && within(pfn, BIOS_PFN, BIOS_PFN_END))
*/ return _PAGE_NX;
#ifdef CONFIG_PCI_BIOS return 0;
if (pcibios_enabled && within(pfn, BIOS_BEGIN >> PAGE_SHIFT, BIOS_END >> PAGE_SHIFT)) }
pgprot_val(forbidden) |= _PAGE_NX; #else
static pgprotval_t protect_pci_bios(unsigned long pfn)
{
return 0;
}
#endif #endif
/* /*
* The kernel text needs to be executable for obvious reasons * The .rodata section needs to be read-only. Using the pfn catches all
* Does not cover __inittext since that is gone later on. On * aliases. This also includes __ro_after_init, so do not enforce until
* 64bit we do not enforce !NX on the low mapping * kernel_set_to_readonly is true.
*/ */
if (within(address, (unsigned long)_text, (unsigned long)_etext)) static pgprotval_t protect_rodata(unsigned long pfn)
pgprot_val(forbidden) |= _PAGE_NX; {
unsigned long start_pfn = __pa_symbol(__start_rodata) >> PAGE_SHIFT;
unsigned long end_pfn = __pa_symbol(__end_rodata) >> PAGE_SHIFT;
/* if (kernel_set_to_readonly && within(pfn, start_pfn, end_pfn))
* The .rodata section needs to be read-only. Using the pfn return _PAGE_RW;
* catches all aliases. This also includes __ro_after_init, return 0;
* so do not enforce until kernel_set_to_readonly is true. }
*/
if (kernel_set_to_readonly && /*
within(pfn, __pa_symbol(__start_rodata) >> PAGE_SHIFT, * Protect kernel text against becoming non executable by forbidding
__pa_symbol(__end_rodata) >> PAGE_SHIFT)) * _PAGE_NX. This protects only the high kernel mapping (_text -> _etext)
pgprot_val(forbidden) |= _PAGE_RW; * out of which the kernel actually executes. Do not protect the low
* mapping.
*
* This does not cover __inittext since that is gone after boot.
*/
static pgprotval_t protect_kernel_text(unsigned long address)
{
if (within(address, (unsigned long)_text, (unsigned long)_etext))
return _PAGE_NX;
return 0;
}
#if defined(CONFIG_X86_64) #if defined(CONFIG_X86_64)
/*
* Once the kernel maps the text as RO (kernel_set_to_readonly is set),
* kernel text mappings for the large page aligned text, rodata sections
* will be always read-only. For the kernel identity mappings covering the
* holes caused by this alignment can be anything that user asks.
*
* This will preserve the large page mappings for kernel text/data at no
* extra cost.
*/
static pgprotval_t protect_kernel_text_ro(unsigned long address)
{
unsigned long end = (unsigned long)__end_rodata_hpage_align;
unsigned long start = (unsigned long)_text;
unsigned int level;
if (!kernel_set_to_readonly || !within(address, start, end))
return 0;
/* /*
* Once the kernel maps the text as RO (kernel_set_to_readonly is set), * Don't enforce the !RW mapping for the kernel text mapping, if
* kernel text mappings for the large page aligned text, rodata sections * the current mapping is already using small page mapping. No
* will be always read-only. For the kernel identity mappings covering * need to work hard to preserve large page mappings in this case.
* the holes caused by this alignment can be anything that user asks.
* *
* This will preserve the large page mappings for kernel text/data * This also fixes the Linux Xen paravirt guest boot failure caused
* at no extra cost. * by unexpected read-only mappings for kernel identity
* mappings. In this paravirt guest case, the kernel text mapping
* and the kernel identity mapping share the same page-table pages,
* so the protections for kernel text and identity mappings have to
* be the same.
*/ */
if (kernel_set_to_readonly && if (lookup_address(address, &level) && (level != PG_LEVEL_4K))
within(address, (unsigned long)_text, return _PAGE_RW;
(unsigned long)__end_rodata_hpage_align)) { return 0;
unsigned int level; }
#else
/* static pgprotval_t protect_kernel_text_ro(unsigned long address)
* Don't enforce the !RW mapping for the kernel text mapping, {
* if the current mapping is already using small page mapping. return 0;
* No need to work hard to preserve large page mappings in this }
* case.
*
* This also fixes the Linux Xen paravirt guest boot failure
* (because of unexpected read-only mappings for kernel identity
* mappings). In this paravirt guest case, the kernel text
* mapping and the kernel identity mapping share the same
* page-table pages. Thus we can't really use different
* protections for the kernel text and identity mappings. Also,
* these shared mappings are made of small page mappings.
* Thus this don't enforce !RW mapping for small page kernel
* text mapping logic will help Linux Xen parvirt guest boot
* as well.
*/
if (lookup_address(address, &level) && (level != PG_LEVEL_4K))
pgprot_val(forbidden) |= _PAGE_RW;
}
#endif #endif
prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); /*
* 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
* right (again, ioremap() on BIOS memory is not uncommon) so this function
* checks and fixes these known static required protection bits.
*/
static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
unsigned long pfn)
{
pgprotval_t forbidden;
/* Operate on the virtual address */
forbidden = protect_kernel_text(address);
forbidden |= protect_kernel_text_ro(address);
return prot; /* Check the PFN directly */
forbidden |= protect_pci_bios(pfn);
forbidden |= protect_rodata(pfn);
return __pgprot(pgprot_val(prot) & ~forbidden);
} }
/* /*
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册