Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

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

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2019 Synopsys.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V2_INTERNAL_H_
#define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V2_INTERNAL_H_

#define AUX_MPU_EN_ENABLE   BIT(30)
#define AUX_MPU_EN_DISABLE  ~BIT(30)

/*
 * The size of the region is a 5-bit field, the three MSB bits are
 * represented in [11:9] and the two LSB bits are represented in [1:0].
 * Together these fields specify the size of the region in bytes:
 * 00000-00011	Reserved
 * 0x4  32		0x5  64		0x6  128	0x7 256
 * 0x8  512		0x9  1k		0xA  2K		0xB 4K
 * 0xC  8K		0xD  16K	0xE  32K	0xF 64K
 * 0x10 128K	0x11 256K	0x12 512K	0x13 1M
 * 0x14 2M		0x15 4M		0x16 8M		0x17 16M
 * 0x18 32M		0x19 64M	0x1A 128M	0x1B 256M
 * 0x1C 512M	0x1D 1G		0x1E 2G		0x1F 4G
 *
 * Bit ... 12 11   10    9 8    3  2  1         0
 *     ------+------------+------+---+-----------+
 *     ...   | SIZE[11:9] | ATTR | R | SIZE[1:0] |
 *     ------+------------+------+---+-----------+
 */
/* arrange size into proper bit field in RDP aux reg*/
#define AUX_MPU_RDP_REGION_SIZE(size)  (((size - 1) & BIT_MASK(2)) | \
					(((size - 1) & (BIT_MASK(3) << 2)) << 7))
/* recover size from bit fields in RDP aux reg*/
#define AUX_MPU_RDP_SIZE_SHIFT(rdp)     ((rdp & BIT_MASK(2)) | (((rdp >> 9) & BIT_MASK(3)) << 2))

#define AUX_MPU_RDB_VALID_MASK BIT(0)
#define AUX_MPU_RDP_ATTR_MASK  (BIT_MASK(6) << 3)
#define AUX_MPU_RDP_SIZE_MASK  ((BIT_MASK(3) << 9) | BIT_MASK(2))

/* For MPU version 2, the minimum protection region size is 2048 bytes */
#if CONFIG_ARC_MPU_VER == 2
#define ARC_FEATURE_MPU_ALIGNMENT_BITS 11
/* For MPU version 3, the minimum protection region size is 32 bytes */
#else
#define ARC_FEATURE_MPU_ALIGNMENT_BITS 5
#endif

/**
 * This internal function initializes a MPU region
 */
static inline void _region_init(uint32_t index, uint32_t region_addr, uint32_t size,
				uint32_t region_attr)
{
	index = index * 2U;

	if (size > 0) {
		uint8_t bits = find_msb_set(size) - 1;

		if (bits < ARC_FEATURE_MPU_ALIGNMENT_BITS) {
			bits = ARC_FEATURE_MPU_ALIGNMENT_BITS;
		}

		if (BIT(bits) < size) {
			bits++;
		}

		region_attr &= ~(AUX_MPU_RDP_SIZE_MASK);
		region_attr |= AUX_MPU_RDP_REGION_SIZE(bits);
		region_addr |= AUX_MPU_RDB_VALID_MASK;
	} else {
		region_addr = 0U;
	}

	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RDP0 + index, region_attr);
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RDB0 + index, region_addr);
}

/**
 * This internal function is utilized by the MPU driver to parse the intent
 * type (i.e. THREAD_STACK_REGION) and return the correct region index.
 */
static inline int get_region_index_by_type(uint32_t type)
{
	/*
	 * The new MPU regions are allocated per type after the statically
	 * configured regions. The type is one-indexed rather than
	 * zero-indexed.
	 *
	 * For ARC MPU v2, the smaller index has higher priority, so the
	 * index is allocated in reverse order. Static regions start from
	 * the biggest index, then thread related regions.
	 *
	 */
	switch (type) {
	case THREAD_STACK_USER_REGION:
		return get_num_regions() - mpu_config.num_regions - THREAD_STACK_REGION;
	case THREAD_STACK_REGION:
	case THREAD_APP_DATA_REGION:
	case THREAD_DOMAIN_PARTITION_REGION:
		/*
		 * Start domain partition region from stack guard region
		 * since stack guard is not supported.
		 */
		return get_num_regions() - mpu_config.num_regions - type + 1;
	default:
		__ASSERT(0, "Unsupported type");
		return -EINVAL;
	}
}

/**
 * This internal function checks if region is enabled or not
 */
static inline bool _is_enabled_region(uint32_t r_index)
{
	return ((z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + r_index * 2U)
		 & AUX_MPU_RDB_VALID_MASK) == AUX_MPU_RDB_VALID_MASK);
}

/**
 * This internal function check if the given buffer in in the region
 */
static inline bool _is_in_region(uint32_t r_index, uint32_t start, uint32_t size)
{
	uint32_t r_addr_start;
	uint32_t r_addr_end;
	uint32_t r_size_lshift;

	r_addr_start = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDB0 + r_index * 2U)
		       & (~AUX_MPU_RDB_VALID_MASK);
	r_size_lshift = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + r_index * 2U)
			& AUX_MPU_RDP_SIZE_MASK;
	r_size_lshift = AUX_MPU_RDP_SIZE_SHIFT(r_size_lshift);
	r_addr_end = r_addr_start  + (1 << (r_size_lshift + 1));

	if (start >= r_addr_start && (start + size) <= r_addr_end) {
		return true;
	}

	return false;
}

/**
 * This internal function check if the region is user accessible or not
 */
static inline bool _is_user_accessible_region(uint32_t r_index, int write)
{
	uint32_t r_ap;

	r_ap = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RDP0 + r_index * 2U);

	r_ap &= AUX_MPU_RDP_ATTR_MASK;

	if (write) {
		return ((r_ap & (AUX_MPU_ATTR_UW | AUX_MPU_ATTR_KW)) ==
			(AUX_MPU_ATTR_UW | AUX_MPU_ATTR_KW));
	}

	return ((r_ap & (AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR)) ==
		(AUX_MPU_ATTR_UR | AUX_MPU_ATTR_KR));
}

#endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V2_INTERNAL_H_ */