Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * 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. System RAM is identity-mapped:
 *
 * +--------------+ <- CONFIG_SRAM_BASE_ADDRESS
 * | Mapping for  |
 * | all RAM      |
 * |              |
 * |              |
 * +--------------+ <- CONFIG_SRAM_BASE_ADDRESS + CONFIG_SRAM_SIZE
 * | Available    |    also the mapping limit as mappings grown downward
 * | virtual mem  |
 * |              |
 * |..............| <- mapping_pos (grows downward as more mappings are made)
 * | Mapping      |
 * +--------------+
 * | Mapping      |
 * +--------------+
 * | ...          |
 * +--------------+
 * | Mapping      |
 * +--------------+ <- CONFIG_SRAM_BASE_ADDRESS + CONFIG_KERNEL_VM_SIZE
 *
 * 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.
  * z_mem_map() mappings start at the end of the address space, and grow
  * downward.
  *
  * TODO: If we ever encounter a board with RAM in high enough memory
  * such that there isn't room in the address space, define mapping_pos
  * and mapping_limit such that we have mappings grow downward from the
  * beginning of system RAM.
  */
static uint8_t *mapping_pos =
		(uint8_t *)((uintptr_t)(CONFIG_SRAM_BASE_ADDRESS +
					CONFIG_KERNEL_VM_SIZE));

/* Lower-limit of virtual address mapping. Immediately below this is the
 * permanent identity mapping for all SRAM.
 */
static uint8_t *mapping_limit =
	(uint8_t *)((uintptr_t)CONFIG_SRAM_BASE_ADDRESS +
		    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 z_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();
}