mirror of
https://github.com/espressif/esp-idf.git
synced 2026-08-11 01:32:09 +03:00
Merge branch 'feat/esp_mmu_map_virt' into 'master'
feat(esp_mm): implement a mapping function to map at given virtual address Closes IDF-15537 and IDF-6132 See merge request espressif/esp-idf!47219
This commit is contained in:
@@ -296,15 +296,57 @@ esp_err_t esp_mmu_map_get_max_consecutive_free_block_size(mmu_mem_caps_t caps, m
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int32_t s_find_available_region(mem_region_t *mem_regions, uint32_t region_nums, size_t size, mmu_mem_caps_t caps, mmu_target_t target)
|
||||
static bool s_is_vaddr_in_region(esp_vaddr_t vaddr, size_t size, mem_region_t *region)
|
||||
{
|
||||
return vaddr >= region->start && vaddr + size <= region->end;
|
||||
}
|
||||
|
||||
static bool s_is_block_overlapped(esp_vaddr_t vaddr, size_t size, mem_block_t *block)
|
||||
{
|
||||
const esp_vaddr_t end = vaddr + size;
|
||||
return (vaddr < block->laddr_end) && (end > block->laddr_start);
|
||||
}
|
||||
|
||||
static bool s_is_range_available(esp_vaddr_t vaddr, size_t size, mem_region_t *region)
|
||||
{
|
||||
/* Browse all the allocated blocks in the region, looking for the one that contains our start address */
|
||||
mem_block_t *mem_block = NULL;
|
||||
TAILQ_FOREACH(mem_block, ®ion->mem_block_head, entries) {
|
||||
/* Skip the dummy blocks */
|
||||
if (mem_block->size == 0) {
|
||||
continue;
|
||||
}
|
||||
if (s_is_block_overlapped(vaddr, size, mem_block)) {
|
||||
return false;
|
||||
}
|
||||
/* Since the entries are sorted, we can stop searching if the start address of the next block is after the end address of our range */
|
||||
if (mem_block->laddr_start >= vaddr + size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If we didn't find any overlap, our range is available */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* The virtual address is optional, passing 0 will search for the first available region. */
|
||||
static int32_t s_find_available_region(mem_region_t *mem_regions, uint32_t region_nums,
|
||||
esp_vaddr_t vaddr_start, size_t size,
|
||||
mmu_mem_caps_t caps, mmu_target_t target)
|
||||
{
|
||||
const esp_vaddr_t linear_addr = vaddr_start & SOC_MMU_LINEAR_ADDR_MASK;
|
||||
int32_t found_region_id = -1;
|
||||
for (int i = 0; i < region_nums; i++) {
|
||||
if (((mem_regions[i].caps & caps) == caps) && ((mem_regions[i].targets & target) == target)) {
|
||||
if (mem_regions[i].max_slot_size >= size) {
|
||||
/* Regardless of the virtual address, check if the region has the proper capabilities, has enough space and is the target */
|
||||
if (((mem_regions[i].caps & caps) == caps) && ((mem_regions[i].targets & target) == target) && mem_regions[i].max_slot_size >= size) {
|
||||
/* If a virtual address is specified and the region contains it, use this region if the range is available */
|
||||
if (vaddr_start == 0) {
|
||||
found_region_id = i;
|
||||
break;
|
||||
}
|
||||
/* If the virtual address is specified, check if the region contains it */
|
||||
if (s_is_vaddr_in_region(linear_addr, size, &mem_regions[i])) {
|
||||
return s_is_range_available(linear_addr, size, &mem_regions[i]) ? i : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found_region_id;
|
||||
@@ -318,7 +360,7 @@ esp_err_t esp_mmu_map_reserve_block_with_caps(size_t size, mmu_mem_caps_t caps,
|
||||
size_t aligned_size = ALIGN_UP_BY(size, CONFIG_MMU_PAGE_SIZE);
|
||||
uint32_t laddr = 0;
|
||||
|
||||
int32_t found_region_id = s_find_available_region(s_mmu_ctx.mem_regions, s_mmu_ctx.num_regions, aligned_size, caps, target);
|
||||
int32_t found_region_id = s_find_available_region(s_mmu_ctx.mem_regions, s_mmu_ctx.num_regions, 0, aligned_size, caps, target);
|
||||
if (found_region_id == -1) {
|
||||
ESP_EARLY_LOGE(TAG, "no such vaddr range");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
@@ -461,26 +503,26 @@ static void IRAM_ATTR NOINLINE_ATTR s_do_mapping(mmu_target_t target, uint32_t v
|
||||
ESP_EARLY_LOGV(TAG, "actual_mapped_len is 0x%"PRIx32, actual_mapped_len);
|
||||
}
|
||||
|
||||
esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target, mmu_mem_caps_t caps, int flags, void **out_ptr)
|
||||
esp_err_t esp_mmu_map_virt(esp_vaddr_t vaddr_start, esp_paddr_t paddr_start, size_t size, mmu_target_t target, mmu_mem_caps_t caps, int flags, void **out_ptr)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
ESP_RETURN_ON_FALSE(out_ptr, ESP_ERR_INVALID_ARG, TAG, "null pointer");
|
||||
mem_block_t *new_block = NULL;
|
||||
#if !SOC_SPIRAM_SUPPORTED || CONFIG_IDF_TARGET_ESP32
|
||||
ESP_RETURN_ON_FALSE(!(target & MMU_TARGET_PSRAM0), ESP_ERR_NOT_SUPPORTED, TAG, "PSRAM is not supported");
|
||||
#endif
|
||||
ESP_RETURN_ON_FALSE((paddr_start % CONFIG_MMU_PAGE_SIZE == 0), ESP_ERR_INVALID_ARG, TAG, "paddr must be rounded up to the nearest multiple of CONFIG_MMU_PAGE_SIZE");
|
||||
ESP_RETURN_ON_FALSE((vaddr_start % CONFIG_MMU_PAGE_SIZE == 0), ESP_ERR_INVALID_ARG, TAG, "vaddr must be rounded up to the nearest multiple of CONFIG_MMU_PAGE_SIZE");
|
||||
ESP_RETURN_ON_ERROR(s_mem_caps_check(caps), TAG, "invalid caps");
|
||||
|
||||
_lock_acquire(&s_mmu_ctx.mutex);
|
||||
mem_block_t *dummy_head = NULL;
|
||||
mem_block_t *dummy_tail = NULL;
|
||||
size_t aligned_size = ALIGN_UP_BY(size, CONFIG_MMU_PAGE_SIZE);
|
||||
int32_t found_region_id = s_find_available_region(s_mmu_ctx.mem_regions, s_mmu_ctx.num_regions, aligned_size, caps, target);
|
||||
int32_t found_region_id = s_find_available_region(s_mmu_ctx.mem_regions, s_mmu_ctx.num_regions, vaddr_start, aligned_size, caps, target);
|
||||
ESP_GOTO_ON_FALSE(found_region_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no such vaddr range");
|
||||
|
||||
//Now we're sure we can find an available block inside a certain region
|
||||
mem_region_t *found_region = &s_mmu_ctx.mem_regions[found_region_id];
|
||||
mem_block_t *new_block = NULL;
|
||||
|
||||
if (TAILQ_EMPTY(&found_region->mem_block_head)) {
|
||||
dummy_head = (mem_block_t *)heap_caps_calloc(1, sizeof(mem_block_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
@@ -536,7 +578,9 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
|
||||
* address.
|
||||
*/
|
||||
const uint32_t new_paddr_offset = paddr_start - mem_block->paddr_start;
|
||||
*out_ptr = (void *)mem_block->vaddr_start + new_paddr_offset;
|
||||
if (out_ptr) {
|
||||
*out_ptr = (void *)mem_block->vaddr_start + new_paddr_offset;
|
||||
}
|
||||
ESP_LOGD(TAG, "paddr block is mapped already, vaddr_start: %p, size: 0x%x", (void *)mem_block->vaddr_start, mem_block->size);
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto err;
|
||||
@@ -558,16 +602,53 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
|
||||
size_t max_slot_len = 0;
|
||||
mem_block_t *found_block = NULL; //This stands for the block we found, whose slot between its prior block is where we will insert the new block to
|
||||
|
||||
/* Convert virtual address to linear address if specified */
|
||||
uint32_t requested_laddr_start = 0;
|
||||
uint32_t requested_laddr_end = 0;
|
||||
if (vaddr_start != 0) {
|
||||
requested_laddr_start = mmu_ll_vaddr_to_laddr(vaddr_start);
|
||||
requested_laddr_end = requested_laddr_start + aligned_size;
|
||||
/* Verify the virtual address is valid for this region and target */
|
||||
uint32_t vaddr_check = 0;
|
||||
if (caps & MMU_MEM_CAP_EXEC) {
|
||||
vaddr_check = mmu_ll_laddr_to_vaddr(requested_laddr_start, MMU_VADDR_INSTRUCTION, target);
|
||||
} else {
|
||||
vaddr_check = mmu_ll_laddr_to_vaddr(requested_laddr_start, MMU_VADDR_DATA, target);
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(vaddr_check == vaddr_start, ESP_ERR_INVALID_ARG, err, TAG, "invalid virtual address for target and caps");
|
||||
/* Verify the linear address is within the region */
|
||||
ESP_GOTO_ON_FALSE(requested_laddr_start >= found_region->start && requested_laddr_end <= found_region->end,
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "virtual address range is outside the region");
|
||||
ESP_GOTO_ON_FALSE(requested_laddr_start >= found_region->free_head,
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "virtual address range is referencing reserved memory");
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(mem_block, &found_region->mem_block_head, entries) {
|
||||
slot_len = mem_block->laddr_start - last_end;
|
||||
|
||||
if (!found) {
|
||||
if (slot_len >= aligned_size) {
|
||||
/* Ignore dummy blocks and any block with size 0 */
|
||||
if (!found && slot_len > 0) {
|
||||
/* If we have no virtual address to map to, we need to find a block that has enough space */
|
||||
if (vaddr_start == 0 && slot_len >= aligned_size) {
|
||||
//Found it
|
||||
found = true;
|
||||
found_block = mem_block;
|
||||
slot_len -= aligned_size;
|
||||
new_block->laddr_start = last_end;
|
||||
} else if (vaddr_start != 0) {
|
||||
/* If we have a virtual address to map to, we need to make sure the range [last_end;mem_block->laddr_start[
|
||||
* contains the range [requested_laddr_start;requested_laddr_end[ */
|
||||
if (last_end <= requested_laddr_start && requested_laddr_end <= mem_block->laddr_start) {
|
||||
found = true;
|
||||
found_block = mem_block;
|
||||
size_t left_len = requested_laddr_start - last_end;
|
||||
size_t right_len = mem_block->laddr_start - requested_laddr_end;
|
||||
slot_len = MAX(left_len, right_len);
|
||||
new_block->laddr_start = requested_laddr_start;
|
||||
} else if (last_end <= requested_laddr_start && requested_laddr_start < mem_block->laddr_end) {
|
||||
/* Another block is already mapping part of our range, trigger an error */
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "cannot map virtual address, range overlaps with existing mapping");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,16 +679,23 @@ esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target,
|
||||
|
||||
//do mapping
|
||||
s_do_mapping(target, new_block->vaddr_start, paddr_start, aligned_size);
|
||||
*out_ptr = (void *)new_block->vaddr_start;
|
||||
if (out_ptr) {
|
||||
*out_ptr = (void *)new_block->vaddr_start;
|
||||
}
|
||||
_lock_release(&s_mmu_ctx.mutex);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (new_block) {
|
||||
free(new_block);
|
||||
}
|
||||
if (dummy_tail) {
|
||||
TAILQ_REMOVE(&found_region->mem_block_head, dummy_tail, entries);
|
||||
free(dummy_tail);
|
||||
}
|
||||
if (dummy_head) {
|
||||
TAILQ_REMOVE(&found_region->mem_block_head, dummy_head, entries);
|
||||
free(dummy_head);
|
||||
}
|
||||
_lock_release(&s_mmu_ctx.mutex);
|
||||
@@ -615,6 +703,12 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target, mmu_mem_caps_t caps, int flags, void **out_ptr)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(out_ptr != NULL, ESP_ERR_INVALID_ARG, TAG, "null pointer");
|
||||
return esp_mmu_map_virt(0, paddr_start, size, target, caps, flags, out_ptr);
|
||||
}
|
||||
|
||||
#if SOC_MMU_PER_EXT_MEM_TARGET
|
||||
FORCE_INLINE_ATTR void s_unmapping_operation(uint32_t vaddr_start, uint32_t size)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -57,6 +57,11 @@ extern "C" {
|
||||
*/
|
||||
typedef uint32_t esp_paddr_t;
|
||||
|
||||
/**
|
||||
* @brief Virtual memory type
|
||||
*/
|
||||
typedef uint32_t esp_vaddr_t;
|
||||
|
||||
/**
|
||||
* @brief Map a physical memory block to external virtual address block, with given capabilities.
|
||||
*
|
||||
@@ -65,7 +70,7 @@ typedef uint32_t esp_paddr_t;
|
||||
* @param[in] target Physical memory target you're going to map to, see `mmu_target_t`
|
||||
* @param[in] caps Memory capabilities, see `mmu_mem_caps_t`
|
||||
* @param[in] flags Mmap flags
|
||||
* @param[out] out_ptr Start address of the mapped virtual memory
|
||||
* @param[out] out_ptr Start address of the mapped virtual memory, must not be NULL
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
@@ -83,6 +88,39 @@ typedef uint32_t esp_paddr_t;
|
||||
*/
|
||||
esp_err_t esp_mmu_map(esp_paddr_t paddr_start, size_t size, mmu_target_t target, mmu_mem_caps_t caps, int flags, void **out_ptr);
|
||||
|
||||
/**
|
||||
* @brief Map a physical memory block to a given virtual address, with capabilities.
|
||||
*
|
||||
* @param[in] vaddr_start Start address of the virtual memory block to map to.
|
||||
* - Set to 0 to let the driver choose any available virtual address automatically.
|
||||
* - Set to a non-zero page-aligned address to request mapping at a specific virtual address.
|
||||
* The address must not fall within an already mapped virtual address range and must fit
|
||||
* entirely within an available virtual address region.
|
||||
* @param[in] paddr_start Start address of the physical memory block, must be page-aligned
|
||||
* @param[in] size Size to be mapped. Size will be rounded up by to the nearest multiple of MMU page size
|
||||
* @param[in] target Physical memory target to map to, see `mmu_target_t`
|
||||
* @param[in] caps Memory capabilities, see `mmu_mem_caps_t`
|
||||
* @param[in] flags Mapping flags
|
||||
* @param[out] out_ptr Start address of the mapped virtual memory, can be NULL
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument, see printed logs
|
||||
* - ESP_ERR_NOT_SUPPORTED: Only on ESP32, PSRAM is not a supported physical memory target
|
||||
* - ESP_ERR_NOT_FOUND: No enough size free block to use
|
||||
* - ESP_ERR_NO_MEM: Out of memory, this API will allocate some heap memory for internal usage
|
||||
* - ESP_ERR_INVALID_STATE: Paddr is mapped already. Only occurs when the to-be-mapped paddr block is totally
|
||||
* enclosed by a previously mapped block (identical scenario behaves similarly).
|
||||
* In this case, `out_ptr` (if not NULL) is set to the corresponding address within
|
||||
* the *previously mapped* virtual block, regardless of the `vaddr_start` requested.
|
||||
* new_block_start new_block_end
|
||||
* |-------- New Block --------|
|
||||
* |--------------- Block ---------------|
|
||||
* block_start block_end
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_mmu_map_virt(esp_vaddr_t vaddr_start, esp_paddr_t paddr_start, size_t size, mmu_target_t target, mmu_mem_caps_t caps, int flags, void **out_ptr);
|
||||
|
||||
/**
|
||||
* @brief Unmap a previously mapped virtual memory block
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#define TEST_BLOCK_SIZE CONFIG_MMU_PAGE_SIZE
|
||||
#define ALIGN_DOWN(num, align) (((uint32_t)num) & ~((align) - 1))
|
||||
|
||||
const static char *TAG = "MMU_TEST";
|
||||
|
||||
@@ -54,6 +55,42 @@ TEST_CASE("Can dump mapped block stats", "[mmu]")
|
||||
TEST_ESP_OK(esp_mmu_unmap(ptr2));
|
||||
}
|
||||
|
||||
TEST_CASE("Can map partition to a given virtual address", "[mmu]")
|
||||
{
|
||||
const esp_partition_t *part = s_get_partition();
|
||||
ESP_LOGI(TAG, "found partition '%s' at offset 0x%"PRIx32" with size 0x%"PRIx32, part->label, part->address, part->size);
|
||||
|
||||
/* Map in the middle of the address space, if the MMU page size is smaller than 64KB, not the whole range is accessible */
|
||||
const uint32_t range_divider = (64 * 1024) / SOC_MMU_PAGE_SIZE;
|
||||
const uint32_t virt_range_size = (SOC_DROM_HIGH - SOC_DROM_LOW) / range_divider;
|
||||
const uint32_t vaddr = SOC_DROM_LOW + virt_range_size / 2;
|
||||
ESP_LOGI(TAG, "mapping to virtual address 0x%x", vaddr);
|
||||
void *ptr0 = NULL;
|
||||
TEST_ESP_OK(esp_mmu_map_virt(vaddr, part->address, TEST_BLOCK_SIZE, MMU_TARGET_FLASH0, MMU_MEM_CAP_READ, 0, &ptr0));
|
||||
TEST_ESP_OK(esp_mmu_unmap(ptr0));
|
||||
|
||||
TEST_ASSERT((uint32_t)ptr0 == vaddr);
|
||||
}
|
||||
|
||||
/**
|
||||
* When PSRAM XIP is enabled, the flash rodata is mapped to the PSRAM's MMU, so it will not be visible to
|
||||
* the NOR flash MMU.
|
||||
*/
|
||||
#if !CONFIG_SPIRAM_XIP_FROM_PSRAM
|
||||
TEST_CASE("Cannot map partition to a reserved addresses", "[mmu]")
|
||||
{
|
||||
const esp_partition_t *part = s_get_partition();
|
||||
ESP_LOGI(TAG, "found partition '%s' at offset 0x%"PRIx32" with size 0x%"PRIx32, part->label, part->address, part->size);
|
||||
|
||||
/* Map in the address space of the flash ROM area, should fail */
|
||||
void *ptr0 = NULL;
|
||||
extern uint8_t _flash_rodata_start[];
|
||||
const esp_vaddr_t addr = ALIGN_DOWN(_flash_rodata_start, CONFIG_MMU_PAGE_SIZE);
|
||||
esp_err_t err = esp_mmu_map_virt(addr, part->address, TEST_BLOCK_SIZE, MMU_TARGET_FLASH0, MMU_MEM_CAP_READ, 0, &ptr0);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, err);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("Can find paddr caps by any paddr offset", "[mmu]")
|
||||
{
|
||||
const esp_partition_t *part = s_get_partition();
|
||||
|
||||
@@ -77,7 +77,7 @@ The virtual memory pool is made up with one or multiple virtual memory regions,
|
||||
- A virtual memory block is a piece of virtual address range that is dynamically mapped.
|
||||
- A slot is the virtual address range between two virtual memory blocks.
|
||||
- A physical memory block is a piece of physical address range that is to-be-mapped or already mapped to a virtual memory block.
|
||||
- Dynamical mapping is done by calling ``esp_mmap`` driver API :cpp:func:`esp_mmu_map`. This API maps the given physical memory block to a virtual memory block which is allocated by the ``esp_mmap`` driver.
|
||||
- Dynamical mapping is done by calling ``esp_mmap`` driver API :cpp:func:`esp_mmu_map` or :cpp:func:`esp_mmu_map_virt`. These map the given physical memory block to a virtual memory block, either automatically chosen or requested by a specific virtual start address.
|
||||
|
||||
|
||||
Relation Between Memory Blocks
|
||||
@@ -120,7 +120,7 @@ Driver Behaviour
|
||||
Memory Map
|
||||
^^^^^^^^^^
|
||||
|
||||
You can call :cpp:func:`esp_mmu_map` to do a dynamical mapping. This API can allocate a certain size of virtual memory block according to the virtual memory capabilities you selected, then map this virtual memory block to the physical memory block as you requested. The ``esp_mmap`` driver supports mapping to one or more types of physical memory, so you should specify the physical memory target when mapping.
|
||||
You can call :cpp:func:`esp_mmu_map` to do a dynamical mapping, or :cpp:func:`esp_mmu_map_virt` if you need a specific virtual start address. These APIs allocate, or place, a virtual memory block of the required size according to the virtual memory capabilities you selected, then map it to the physical memory block you requested. The ``esp_mmap`` driver supports mapping to one or more types of physical memory, so you should specify the physical memory target when mapping.
|
||||
|
||||
By default, physical memory blocks and virtual memory blocks are one-to-one mapped. This means, when calling :cpp:func:`esp_mmu_map`:
|
||||
|
||||
@@ -133,6 +133,12 @@ Specially, you can use :c:macro:`ESP_MMU_MMAP_FLAG_PADDR_SHARED`. This flag stan
|
||||
* If it is the overlapped scenario, this API will allocate a new virtual memory block as requested, then map to the given physical memory block.
|
||||
|
||||
|
||||
Mapping at a chosen virtual address
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:cpp:func:`esp_mmu_map_virt` maps a physical block the same way as :cpp:func:`esp_mmu_map`, but you can pass the virtual start address as ``vaddr_start``. Passing a value of ``0`` for ``vaddr_start`` will let the driver allocate any suitable free block. With a non-zero ``vaddr_start`` value, the whole range must lie in an unmapped slot for the given capabilities and target, and the address must be valid for the MMU view implied by ``caps``.
|
||||
|
||||
|
||||
Memory Unmap
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
||||
Reference in New Issue
Block a user