mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Refactor setup_frametable_mappings() into init_frametable(), modeled after x86's implementation. Instead of mapping one contiguous frametable covering ram_start to ram_end (including holes), iterate the pdx_group_valid bitmap to allocate and map frametable memory only for valid PDX groups, skipping gaps in the physical address space. This reduces memory consumption on systems with sparse RAM layouts by not allocating frametable entries for non-existent memory regions. The chunk allocator rounds chunk_size up to PAGE_SIZE only, rather than to a larger mapping granularity, to avoid overshooting past chunk boundaries into subsequent gaps or valid regions. This rounding has no impact for in-loop chunks given that chunk size is a multiple of 14MB on Arm64 and 2MB on Arm32. The rounding matters only for the last out-of-loop chunk. Physical allocations prefer 32MB alignment so that map_pages_to_xen() can use the contiguous bit for larger TLB entries where virtual alignment also permits. Fall back to 2MB if the chunk is smaller than 32MB. Add a comment explaining why we don't use pdx_to_page(). For complete discussion see [1]. As ram_end is no longer needed by init_frametable(), drop the now-dead ram_end/bank_end computation from setup_mm(). Update the MPU implementation to match the new init_frametable() signature. Since MPU has no virtual address translation (ma == va), hole-skipping is not possible and the frametable remains a single contiguous allocation. [1] https://lore.kernel.org/xen-devel/20260430125103.401811-1-michal.orzel@amd.com/T/#m803025eb6720a1425443dd0f8e72be93ef02f344 Signed-off-by: Michal Orzel <michal.orzel@amd.com> Reviewed-by: Luca Fancellu <luca.fancellu@arm.com> Tested-by: Luca Fancellu <luca.fancellu@arm.com> Acked-by: Stefano Stabellini <sstabellini@kernel.org>
617 lines
17 KiB
C
617 lines
17 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
#include <xen/bug.h>
|
|
#include <xen/errno.h>
|
|
#include <xen/init.h>
|
|
#include <xen/lib.h>
|
|
#include <xen/mm.h>
|
|
#include <xen/pfn.h>
|
|
#include <xen/sizes.h>
|
|
#include <xen/spinlock.h>
|
|
#include <xen/types.h>
|
|
#include <asm/mpu.h>
|
|
#include <asm/mpu/mm.h>
|
|
#include <asm/page.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/sysregs.h>
|
|
|
|
#define MPU_ATTR_XN_RO_MISMATCH -1
|
|
#define MPU_ATTR_AI_MISMATCH -2
|
|
|
|
struct page_info *frame_table;
|
|
|
|
/* Maximum number of supported MPU memory regions by the EL2 MPU. */
|
|
uint8_t __ro_after_init max_mpu_regions;
|
|
|
|
/*
|
|
* Bitmap xen_mpumap_mask is to record the usage of EL2 MPU memory regions.
|
|
* Bit 0 represents MPU memory region 0, bit 1 represents MPU memory
|
|
* region 1, ..., and so on.
|
|
* If a MPU memory region gets enabled, set the according bit to 1.
|
|
*/
|
|
DECLARE_BITMAP(xen_mpumap_mask, MAX_MPU_REGION_NR) \
|
|
__cacheline_aligned __section(".data");
|
|
|
|
/* EL2 Xen MPU memory region mapping table. */
|
|
pr_t __cacheline_aligned __section(".data") xen_mpumap[MAX_MPU_REGION_NR];
|
|
|
|
DEFINE_SPINLOCK(xen_mpumap_lock);
|
|
|
|
static void __init __maybe_unused build_assertions(void)
|
|
{
|
|
/*
|
|
* Unlike MMU, MPU does not use pages for translation. However, we continue
|
|
* to use PAGE_SIZE to denote 4KB. This is so that the existing memory
|
|
* management based on pages, continue to work for now.
|
|
*/
|
|
BUILD_BUG_ON(PAGE_SIZE != SZ_4K);
|
|
}
|
|
|
|
pr_t pr_of_addr(paddr_t base, paddr_t limit, unsigned int flags)
|
|
{
|
|
unsigned int attr_idx = PAGE_AI_MASK(flags);
|
|
prbar_t prbar;
|
|
prlar_t prlar;
|
|
pr_t region;
|
|
|
|
/* Build up value for PRBAR_EL2. */
|
|
prbar = (prbar_t) {
|
|
.reg = {
|
|
#ifdef CONFIG_ARM_64
|
|
.xn_0 = 0,
|
|
#endif
|
|
.xn = PAGE_XN_MASK(flags),
|
|
.ap_0 = 0,
|
|
.ro = PAGE_RO_MASK(flags)
|
|
}};
|
|
|
|
switch ( attr_idx )
|
|
{
|
|
/*
|
|
* ARM ARM: Shareable, Inner Shareable, and Outer Shareable Normal memory
|
|
* (DDI 0487L.a B2.10.1.1.1 Note section):
|
|
*
|
|
* Because all data accesses to Non-cacheable locations are data coherent
|
|
* to all observers, Non-cacheable locations are always treated as Outer
|
|
* Shareable
|
|
*
|
|
* ARM ARM: Device memory (DDI 0487L.a B2.10.2)
|
|
*
|
|
* All of these memory types have the following properties:
|
|
* [...]
|
|
* - Data accesses to memory locations are coherent for all observers in
|
|
* the system, and correspondingly are treated as being Outer Shareable
|
|
*/
|
|
case MT_NORMAL_NC:
|
|
/* Fall through */
|
|
case MT_DEVICE_nGnRnE:
|
|
/* Fall through */
|
|
case MT_DEVICE_nGnRE:
|
|
prbar.reg.sh = LPAE_SH_OUTER;
|
|
break;
|
|
default:
|
|
/* Xen mappings are SMP coherent */
|
|
prbar.reg.sh = LPAE_SH_INNER;
|
|
break;
|
|
}
|
|
|
|
/* Build up value for PRLAR_EL2. */
|
|
prlar = (prlar_t) {
|
|
.reg = {
|
|
#ifdef CONFIG_ARM_64
|
|
.ns = 0, /* Hyp mode is in secure world */
|
|
#endif
|
|
.ai = attr_idx,
|
|
.en = 1, /* Region enabled */
|
|
}};
|
|
|
|
/* Build up MPU memory region. */
|
|
region = (pr_t) {
|
|
.prbar = prbar,
|
|
.prlar = prlar,
|
|
.refcount = 0,
|
|
};
|
|
|
|
/* Set base address and limit address. */
|
|
pr_set_base(®ion, base);
|
|
pr_set_limit(®ion, limit);
|
|
|
|
return region;
|
|
}
|
|
|
|
int mpumap_contains_region(pr_t *table, uint8_t nr_regions, paddr_t base,
|
|
paddr_t limit, uint8_t *index)
|
|
{
|
|
ASSERT(index);
|
|
*index = INVALID_REGION_IDX;
|
|
|
|
/*
|
|
* The caller supplies a half-open interval [base, limit), i.e. limit is the
|
|
* first byte *after* the region. Require limit strictly greater than base,
|
|
* which is necessarily a non-empty region.
|
|
*/
|
|
ASSERT(base < limit);
|
|
|
|
/*
|
|
* Internally we use inclusive bounds, so convert range to [base, limit-1].
|
|
* The prior assertion guarantees the subtraction will not underflow.
|
|
*/
|
|
limit = limit - 1;
|
|
|
|
for ( uint8_t i = 0; i < nr_regions; i++ )
|
|
{
|
|
paddr_t iter_base = pr_get_base(&table[i]);
|
|
paddr_t iter_limit = pr_get_limit(&table[i]);
|
|
|
|
/* Skip invalid (disabled) regions */
|
|
if ( !region_is_valid(&table[i]) )
|
|
continue;
|
|
|
|
/* No match */
|
|
if ( (iter_limit < base) || (iter_base > limit) )
|
|
continue;
|
|
|
|
/* Exact match */
|
|
if ( (iter_base == base) && (iter_limit == limit) )
|
|
{
|
|
*index = i;
|
|
return MPUMAP_REGION_FOUND;
|
|
}
|
|
|
|
/* Inclusive match */
|
|
if ( (base >= iter_base) && (limit <= iter_limit) )
|
|
{
|
|
*index = i;
|
|
return MPUMAP_REGION_INCLUSIVE;
|
|
}
|
|
|
|
/* Overlap */
|
|
printk("Range %#"PRIpaddr" - %#"PRIpaddr" overlaps with the existing region %#"PRIpaddr" - %#"PRIpaddr"\n",
|
|
base, limit + 1, iter_base, iter_limit + 1);
|
|
return MPUMAP_REGION_OVERLAP;
|
|
}
|
|
|
|
return MPUMAP_REGION_NOTFOUND;
|
|
}
|
|
|
|
static int is_mm_attr_match(pr_t *region, unsigned int attributes)
|
|
{
|
|
if ( (region->prbar.reg.xn != PAGE_XN_MASK(attributes)) ||
|
|
(region->prbar.reg.ro != PAGE_RO_MASK(attributes)) )
|
|
return MPU_ATTR_XN_RO_MISMATCH;
|
|
|
|
if ( region->prlar.reg.ai != PAGE_AI_MASK(attributes) )
|
|
return MPU_ATTR_AI_MISMATCH;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Allocate a contiguous frame table covering ram_start through max_pdx.
|
|
* Unlike the MMU version, MPU cannot skip holes because there is no virtual
|
|
* address translation (ma == va).
|
|
*/
|
|
void __init init_frametable(paddr_t ram_start)
|
|
{
|
|
unsigned long nr_pdxs, frametable_size;
|
|
mfn_t base_mfn;
|
|
|
|
/*
|
|
* The size of paddr_t should be sufficient for the complete range of
|
|
* physical address.
|
|
*/
|
|
BUILD_BUG_ON((sizeof(paddr_t) * BITS_PER_BYTE) < PADDR_BITS);
|
|
BUILD_BUG_ON(sizeof(struct page_info) != PAGE_INFO_SIZE);
|
|
|
|
frametable_base_pdx = mfn_to_pdx(maddr_to_mfn(ram_start));
|
|
nr_pdxs = max_pdx - frametable_base_pdx;
|
|
frametable_size = nr_pdxs * sizeof(struct page_info);
|
|
|
|
if ( frametable_size > FRAMETABLE_SIZE )
|
|
panic("Frametable too small\n");
|
|
|
|
frametable_size = ROUNDUP(frametable_size, PAGE_SIZE);
|
|
|
|
base_mfn = alloc_boot_pages(frametable_size >> PAGE_SHIFT, 1);
|
|
frame_table = (struct page_info *)mfn_to_virt(mfn_x(base_mfn));
|
|
|
|
memset(&frame_table[0], 0, nr_pdxs * sizeof(struct page_info));
|
|
memset(&frame_table[nr_pdxs], -1,
|
|
frametable_size - (nr_pdxs * sizeof(struct page_info)));
|
|
}
|
|
|
|
/*
|
|
* Allocate an entry for a new EL2 MPU region in the bitmap xen_mpumap_mask.
|
|
* @param idx Set to the index of the allocated EL2 MPU region on success.
|
|
* @return 0 on success, otherwise -ENOENT on failure.
|
|
*/
|
|
static int xen_mpumap_alloc_entry(uint8_t *idx)
|
|
{
|
|
ASSERT(spin_is_locked(&xen_mpumap_lock));
|
|
|
|
*idx = find_first_zero_bit(xen_mpumap_mask, max_mpu_regions);
|
|
if ( *idx == max_mpu_regions )
|
|
{
|
|
printk(XENLOG_ERR "EL2 MPU memory region mapping pool exhausted\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
set_bit(*idx, xen_mpumap_mask);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Disable and remove an MPU region from the data structure and MPU registers.
|
|
*
|
|
* @param index Index of the MPU region to be disabled.
|
|
*/
|
|
static void disable_mpu_region_from_index(uint8_t index)
|
|
{
|
|
ASSERT(spin_is_locked(&xen_mpumap_lock));
|
|
ASSERT(index != INVALID_REGION_IDX);
|
|
|
|
if ( !region_is_valid(&xen_mpumap[index]) )
|
|
{
|
|
printk(XENLOG_WARNING
|
|
"MPU memory region[%u] is already disabled\n", index);
|
|
return;
|
|
}
|
|
|
|
/* Zeroing the region will also zero the region enable */
|
|
memset(&xen_mpumap[index], 0, sizeof(pr_t));
|
|
clear_bit(index, xen_mpumap_mask);
|
|
|
|
/*
|
|
* Both Armv8-R AArch64 and AArch32 have direct access to the enable bit for
|
|
* MPU regions numbered from 0 to 31.
|
|
*/
|
|
if ( (index & PRENR_MASK) != 0 )
|
|
{
|
|
/* Clear respective bit */
|
|
register_t val = READ_SYSREG(PRENR_EL2) & (~(1UL << index));
|
|
|
|
WRITE_SYSREG(val, PRENR_EL2);
|
|
}
|
|
else
|
|
write_protection_region(&xen_mpumap[index], index);
|
|
}
|
|
|
|
/*
|
|
* Free a xen_mpumap entry given the index. A mpu region is actually disabled
|
|
* when the refcount is 0 and the region type is MPUMAP_REGION_FOUND.
|
|
*
|
|
* @param idx Index of the mpumap entry.
|
|
* @param region_found_type MPUMAP_REGION_* value.
|
|
* @return 0 on success, otherwise negative on error.
|
|
*/
|
|
static int xen_mpumap_free_entry(uint8_t idx, int region_found_type)
|
|
{
|
|
ASSERT(spin_is_locked(&xen_mpumap_lock));
|
|
ASSERT(idx != INVALID_REGION_IDX);
|
|
ASSERT(MPUMAP_REGION_OVERLAP != region_found_type);
|
|
|
|
if ( MPUMAP_REGION_NOTFOUND == region_found_type )
|
|
{
|
|
printk(XENLOG_ERR "Cannot remove entry that does not exist\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ( xen_mpumap[idx].refcount )
|
|
{
|
|
xen_mpumap[idx].refcount -= 1;
|
|
return 0;
|
|
}
|
|
|
|
if ( MPUMAP_REGION_FOUND != region_found_type )
|
|
{
|
|
printk(XENLOG_ERR "Cannot remove a partial region\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
disable_mpu_region_from_index(idx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Update the entry in the MPU memory region mapping table (xen_mpumap) for the
|
|
* given memory range and flags, creating one if none exists.
|
|
*
|
|
* @param base Base address (inclusive).
|
|
* @param limit Limit address (exclusive).
|
|
* @param flags Region attributes (a combination of PAGE_HYPERVISOR_XXX)
|
|
* @return 0 on success, otherwise negative on error.
|
|
*/
|
|
static int xen_mpumap_update_entry(paddr_t base, paddr_t limit,
|
|
unsigned int flags)
|
|
{
|
|
bool flags_has_page_present;
|
|
uint8_t idx;
|
|
int rc;
|
|
|
|
ASSERT(spin_is_locked(&xen_mpumap_lock));
|
|
|
|
rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, base, limit, &idx);
|
|
if ( rc < 0 )
|
|
return -EINVAL;
|
|
|
|
flags_has_page_present = flags & _PAGE_PRESENT;
|
|
|
|
/*
|
|
* Currently, we only support removing/modifying a *WHOLE* MPU memory
|
|
* region. Part-region removal/modification is not supported as in the worst
|
|
* case it will leave two/three fragments behind.
|
|
*/
|
|
if ( flags_has_page_present && (rc >= MPUMAP_REGION_FOUND) )
|
|
{
|
|
int attr_match = is_mm_attr_match(&xen_mpumap[idx], flags);
|
|
|
|
/* We do not support modifying AI attribute. */
|
|
if ( MPU_ATTR_AI_MISMATCH == attr_match )
|
|
{
|
|
printk(XENLOG_ERR
|
|
"Modifying AI attribute is not supported\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Attributes RO and XN can be changed only by the full region.
|
|
* Attributes that match can continue and just increment refcount.
|
|
*/
|
|
if ( MPU_ATTR_XN_RO_MISMATCH == attr_match )
|
|
{
|
|
if ( rc == MPUMAP_REGION_INCLUSIVE )
|
|
{
|
|
printk(XENLOG_ERR
|
|
"Cannot modify partial region attributes\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ( xen_mpumap[idx].refcount != 0 )
|
|
{
|
|
printk(XENLOG_ERR
|
|
"Cannot modify RO,XN attributes for a region mapped multiple times\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Set new attributes */
|
|
xen_mpumap[idx].prbar.reg.ro = PAGE_RO_MASK(flags);
|
|
xen_mpumap[idx].prbar.reg.xn = PAGE_XN_MASK(flags);
|
|
|
|
write_protection_region(&xen_mpumap[idx], idx);
|
|
return 0;
|
|
}
|
|
|
|
/* Check for overflow of refcount before incrementing. */
|
|
if ( xen_mpumap[idx].refcount == 0xFF )
|
|
{
|
|
printk("Cannot allocate region as it would cause refcount overflow\n");
|
|
return -ENOENT;
|
|
}
|
|
xen_mpumap[idx].refcount += 1;
|
|
}
|
|
|
|
/* We are inserting a mapping => Create new region. */
|
|
if ( flags_has_page_present && (MPUMAP_REGION_NOTFOUND == rc) )
|
|
{
|
|
rc = xen_mpumap_alloc_entry(&idx);
|
|
if ( rc )
|
|
return -ENOENT;
|
|
|
|
xen_mpumap[idx] = pr_of_addr(base, limit, flags);
|
|
|
|
write_protection_region(&xen_mpumap[idx], idx);
|
|
}
|
|
|
|
/* Removing a mapping */
|
|
if ( !flags_has_page_present )
|
|
{
|
|
if ( rc == MPUMAP_REGION_NOTFOUND )
|
|
{
|
|
printk("Cannot remove an entry that does not exist\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return xen_mpumap_free_entry(idx, rc);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xen_mpumap_update(paddr_t base, paddr_t limit, unsigned int flags)
|
|
{
|
|
int rc;
|
|
|
|
if ( flags_has_rwx(flags) )
|
|
{
|
|
printk("Mappings should not be both Writeable and Executable\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ( base >= limit )
|
|
{
|
|
printk("Base address %#"PRIpaddr" must be smaller than limit address %#"PRIpaddr"\n",
|
|
base, limit);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ( !IS_ALIGNED(base, PAGE_SIZE) || !IS_ALIGNED(limit, PAGE_SIZE) )
|
|
{
|
|
printk("base address %#"PRIpaddr", or limit address %#"PRIpaddr" is not page aligned\n",
|
|
base, limit);
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_lock(&xen_mpumap_lock);
|
|
|
|
rc = xen_mpumap_update_entry(base, limit, flags);
|
|
if ( !rc )
|
|
context_sync_mpu();
|
|
|
|
spin_unlock(&xen_mpumap_lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int destroy_xen_mappings(unsigned long s, unsigned long e)
|
|
{
|
|
ASSERT(IS_ALIGNED(s, PAGE_SIZE));
|
|
ASSERT(IS_ALIGNED(e, PAGE_SIZE));
|
|
ASSERT(s < e);
|
|
|
|
return xen_mpumap_update(s, e, 0);
|
|
}
|
|
|
|
int destroy_xen_mapping_containing(paddr_t s)
|
|
{
|
|
int rc;
|
|
uint8_t idx;
|
|
|
|
ASSERT(IS_ALIGNED(s, PAGE_SIZE));
|
|
|
|
spin_lock(&xen_mpumap_lock);
|
|
|
|
rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, s, s + PAGE_SIZE,
|
|
&idx);
|
|
|
|
/*
|
|
* Since only entire regions can be freed using `xen_mpumap_free_entry` we
|
|
* must first check the region exists.
|
|
*/
|
|
if ( MPUMAP_REGION_NOTFOUND == rc )
|
|
{
|
|
printk(XENLOG_ERR "Cannot remove entry that does not exist");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* As we are unmapping entire region use MPUMAP_REGION_FOUND instead */
|
|
rc = xen_mpumap_free_entry(idx, MPUMAP_REGION_FOUND);
|
|
if ( !rc )
|
|
context_sync_mpu();
|
|
out:
|
|
spin_unlock(&xen_mpumap_lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int map_pages_to_xen(unsigned long virt, mfn_t mfn, unsigned long nr_mfns,
|
|
unsigned int flags)
|
|
{
|
|
/* MPU systems have no translation, ma == va, so pass virt directly */
|
|
return xen_mpumap_update(virt, mfn_to_maddr(mfn_add(mfn, nr_mfns)), flags);
|
|
}
|
|
|
|
/*
|
|
* Heap must be statically configured in Device Tree through "xen,static-heap"
|
|
* on MPU systems, use setup_mm_helper() for that.
|
|
*/
|
|
void __init setup_mm_helper(void)
|
|
{
|
|
const struct membanks *reserved_mem = bootinfo_get_reserved_mem();
|
|
unsigned int bank = 0;
|
|
|
|
for ( ; bank < reserved_mem->nr_banks; bank++ )
|
|
{
|
|
if ( reserved_mem->bank[bank].type == MEMBANK_STATIC_HEAP )
|
|
{
|
|
paddr_t bank_start = round_pgup(reserved_mem->bank[bank].start);
|
|
paddr_t bank_size = round_pgdown(reserved_mem->bank[bank].size);
|
|
paddr_t bank_end = bank_start + bank_size;
|
|
|
|
/* Map static heap with one MPU protection region */
|
|
if ( xen_mpumap_update(bank_start, bank_end, PAGE_HYPERVISOR) )
|
|
panic("Failed to map static heap\n");
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bank == reserved_mem->nr_banks )
|
|
panic("No static heap memory bank found\n");
|
|
}
|
|
|
|
int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf)
|
|
{
|
|
return xen_mpumap_update(s, e, nf);
|
|
}
|
|
|
|
void dump_hyp_walk(vaddr_t addr)
|
|
{
|
|
BUG_ON("unimplemented");
|
|
}
|
|
|
|
/* Release all __init and __initdata ranges to be reused */
|
|
void free_init_memory(void)
|
|
{
|
|
unsigned long inittext_end = (unsigned long)__init_data_begin;
|
|
unsigned long len = __init_end - __init_begin;
|
|
uint8_t idx;
|
|
int rc;
|
|
|
|
/* Modify inittext region to be read/write instead of read/execute. */
|
|
rc = modify_xen_mappings((unsigned long)__init_begin, inittext_end,
|
|
PAGE_HYPERVISOR_RW);
|
|
if ( rc )
|
|
panic("Unable to map RW the init text section (rc = %d)\n", rc);
|
|
|
|
/*
|
|
* From now on, init will not be used for execution anymore,
|
|
* so nuke the instruction cache to remove entries related to init.
|
|
*/
|
|
invalidate_icache_local();
|
|
|
|
/*
|
|
* The initdata region already has read/write permissions so it can just be
|
|
* zeroed out.
|
|
*/
|
|
memset(__init_begin, 0, len);
|
|
|
|
rc = destroy_xen_mappings((unsigned long)__init_begin, inittext_end);
|
|
if ( rc )
|
|
panic("Unable to remove init text section (rc = %d)\n", rc);
|
|
|
|
/*
|
|
* The initdata and bss sections are mapped using a single MPU region, so
|
|
* modify the start of this region to remove the initdata section.
|
|
*/
|
|
spin_lock(&xen_mpumap_lock);
|
|
|
|
rc = mpumap_contains_region(xen_mpumap, max_mpu_regions,
|
|
(unsigned long)__init_data_begin,
|
|
(unsigned long)__bss_end,
|
|
&idx);
|
|
if ( rc < MPUMAP_REGION_FOUND )
|
|
panic("Unable to find bss data section (rc = %d)\n", rc);
|
|
|
|
/* bss data section is shrunk and now starts from __bss_start */
|
|
pr_set_base(&xen_mpumap[idx], (unsigned long)__bss_start);
|
|
|
|
write_protection_region(&xen_mpumap[idx], idx);
|
|
context_sync_mpu();
|
|
|
|
spin_unlock(&xen_mpumap_lock);
|
|
}
|
|
|
|
void __iomem *ioremap_attr(paddr_t start, size_t len, unsigned int flags)
|
|
{
|
|
paddr_t start_pg = round_pgdown(start);
|
|
paddr_t end_pg = round_pgup(start + len);
|
|
|
|
if ( xen_mpumap_update(start_pg, end_pg, flags) )
|
|
return NULL;
|
|
|
|
/* Mapped or already mapped */
|
|
return maddr_to_virt(start);
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-file-style: "BSD"
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|