You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
3.7 KiB
135 lines
3.7 KiB
/* |
|
* Copyright (c) 2020 Intel Corporation |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
* |
|
* Routines for managing virtual address spaces |
|
*/ |
|
|
|
#include <stdint.h> |
|
#include <kernel_arch_interface.h> |
|
#include <spinlock.h> |
|
|
|
#define LOG_LEVEL CONFIG_KERNEL_LOG_LEVEL |
|
#include <logging/log.h> |
|
LOG_MODULE_DECLARE(os); |
|
|
|
/* Spinlock to protect any globals in this file and serialize page table |
|
* updates in arch code |
|
*/ |
|
static struct k_spinlock mm_lock; |
|
|
|
/* |
|
* Overall virtual memory map: |
|
* |
|
* +--------------+ <- CONFIG_KERNEL_VM_BASE |
|
* | Mapping for | |
|
* | all RAM | |
|
* | | |
|
* | | |
|
* +--------------+ <- mapping_limit |
|
* | Available | |
|
* | virtual mem | |
|
* | | |
|
* |..............| <- mapping_pos (grows downward as more mappings are made) |
|
* | Mapping | |
|
* +--------------+ |
|
* | Mapping | |
|
* +--------------+ |
|
* | ... | |
|
* +--------------+ |
|
* | Mapping | |
|
* +--------------+ <- CONFIG_KERNEL_VM_LIMIT |
|
* |
|
* At the moment we just have one area for mappings and they are permanent. |
|
* This is under heavy development and may change. |
|
*/ |
|
|
|
/* Current position for memory mappings in kernel memory. |
|
* At the moment, all kernel memory mappings are permanent. |
|
* k_mem_map() mappings start at the end of the address space, and grow |
|
* downward. |
|
* |
|
* The Kconfig value is inclusive so add one, even if it wraps around to 0. |
|
*/ |
|
static uint8_t *mapping_pos = |
|
(uint8_t *)((uintptr_t)CONFIG_KERNEL_VM_LIMIT + 1UL); |
|
|
|
/* Lower-limit of virtual address mapping. Immediately below this is the |
|
* permanent mapping for all SRAM. |
|
*/ |
|
static uint8_t *mapping_limit = (uint8_t *)((uintptr_t)CONFIG_KERNEL_VM_BASE + |
|
KB((size_t)CONFIG_SRAM_SIZE)); |
|
|
|
size_t k_mem_region_align(uintptr_t *aligned_addr, size_t *aligned_size, |
|
uintptr_t phys_addr, size_t size, size_t align) |
|
{ |
|
size_t addr_offset; |
|
|
|
/* The actual mapped region must be page-aligned. Round down the |
|
* physical address and pad the region size appropriately |
|
*/ |
|
*aligned_addr = ROUND_DOWN(phys_addr, align); |
|
addr_offset = phys_addr - *aligned_addr; |
|
*aligned_size = ROUND_UP(size + addr_offset, align); |
|
|
|
return addr_offset; |
|
} |
|
|
|
void k_mem_map(uint8_t **virt_addr, uintptr_t phys_addr, size_t size, |
|
uint32_t flags) |
|
{ |
|
uintptr_t aligned_addr, addr_offset; |
|
size_t aligned_size; |
|
int ret; |
|
k_spinlock_key_t key; |
|
uint8_t *dest_virt; |
|
|
|
addr_offset = k_mem_region_align(&aligned_addr, &aligned_size, |
|
phys_addr, size, |
|
CONFIG_MMU_PAGE_SIZE); |
|
|
|
key = k_spin_lock(&mm_lock); |
|
|
|
/* Carve out some unused virtual memory from the top of the |
|
* address space |
|
*/ |
|
if ((mapping_pos - aligned_size) < mapping_limit) { |
|
LOG_ERR("insufficient kernel virtual address space"); |
|
goto fail; |
|
} |
|
mapping_pos -= aligned_size; |
|
dest_virt = mapping_pos; |
|
|
|
LOG_DBG("arch_mem_map(%p, 0x%lx, %zu, %x) offset %lu\n", dest_virt, |
|
aligned_addr, aligned_size, flags, addr_offset); |
|
__ASSERT(dest_virt != NULL, "NULL page memory mapping"); |
|
__ASSERT(aligned_size != 0, "0-length mapping at 0x%lx", aligned_addr); |
|
__ASSERT((uintptr_t)dest_virt < |
|
((uintptr_t)dest_virt + (aligned_size - 1)), |
|
"wraparound for virtual address %p (size %zu)", |
|
dest_virt, size); |
|
__ASSERT(aligned_addr < (aligned_addr + (size - 1)), |
|
"wraparound for physical address 0x%lx (size %zu)", |
|
aligned_addr, size); |
|
|
|
ret = arch_mem_map(dest_virt, aligned_addr, aligned_size, flags); |
|
k_spin_unlock(&mm_lock, key); |
|
|
|
if (ret == 0) { |
|
*virt_addr = dest_virt + addr_offset; |
|
} else { |
|
/* This happens if there is an insurmountable problem |
|
* with the selected cache modes or access flags |
|
* with no safe fallback |
|
*/ |
|
|
|
LOG_ERR("arch_mem_map() to %p returned %d", dest_virt, ret); |
|
goto fail; |
|
} |
|
return; |
|
fail: |
|
LOG_ERR("memory mapping 0x%lx (size %zu, flags 0x%x) failed", |
|
phys_addr, size, flags); |
|
k_panic(); |
|
}
|
|
|