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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
/*
 * Copyright (c) 2019 Synopsys.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#ifndef ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V3_INTERNAL_H_
#define ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V3_INTERNAL_H_

#define AUX_MPU_RPER_SID1       0x10000
/* valid mask: SID1+secure+valid */
#define AUX_MPU_RPER_VALID_MASK ((0x1) | AUX_MPU_RPER_SID1 | AUX_MPU_ATTR_S)

#define AUX_MPU_RPER_ATTR_MASK (0x1FF)

/* For MPU version 3, the minimum protection region size is 32 bytes */
#define ARC_FEATURE_MPU_ALIGNMENT_BITS 5

#define CALC_REGION_END_ADDR(start, size) \
	(start + size - (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS))

/* ARC MPU version 3 does not support mpu region overlap in hardware
 * so if we want to allocate MPU region dynamically, e.g. thread stack,
 * memory domain from a background region, a dynamic region splitting
 * approach is designed. pls see comments in
 *          _dynamic_region_allocate_and_init
 * But this approach has an impact on performance of thread switch.
 * As a trade off, we can use the default mpu region as the background region
 * to avoid the dynamic region splitting. This will give more privilege to
 * codes in kernel mode which can access the memory region not covered by
 * explicit mpu entry. Considering  memory protection is mainly used to
 * isolate malicious codes in user mode, it makes sense to get better
 * thread switch performance through default mpu region.
 * CONFIG_MPU_GAP_FILLING is used to turn this on/off.
 *
 */
#if defined(CONFIG_MPU_GAP_FILLING)

#if defined(CONFIG_USERSPACE) && defined(CONFIG_MPU_STACK_GUARD)
/* 1 for stack guard , 1 for user thread, 1 for split */
#define MPU_REGION_NUM_FOR_THREAD 3
#elif defined(CONFIG_USERSPACE) || defined(CONFIG_MPU_STACK_GUARD)
/* 1 for stack guard or user thread stack , 1 for split */
#define MPU_REGION_NUM_FOR_THREAD 2
#else
#define MPU_REGION_NUM_FOR_THREAD 0
#endif

#define MPU_DYNAMIC_REGION_AREAS_NUM 2

/**
 * @brief internal structure holding information of
 * memory areas where dynamic MPU programming is allowed.
 */
struct dynamic_region_info {
	uint8_t index;
	uint32_t base;
	uint32_t size;
	uint32_t attr;
};

static uint8_t dynamic_regions_num;
static uint8_t dynamic_region_index;

/**
 * Global array, holding the MPU region index of
 * the memory region inside which dynamic memory
 * regions may be configured.
 */
static struct dynamic_region_info dyn_reg_info[MPU_DYNAMIC_REGION_AREAS_NUM];
#endif /* CONFIG_MPU_GAP_FILLING */

static uint8_t static_regions_num;

#ifdef CONFIG_ARC_NORMAL_FIRMWARE
/* \todo through secure service to access mpu */
static inline void _region_init(uint32_t index, uint32_t region_addr, uint32_t size,
				uint32_t region_attr)
{
}

static inline void _region_set_attr(uint32_t index, uint32_t attr)
{

}

static inline uint32_t _region_get_attr(uint32_t index)
{
	return 0;
}

static inline uint32_t _region_get_start(uint32_t index)
{
	return 0;
}

static inline void _region_set_start(uint32_t index, uint32_t start)
{

}

static inline uint32_t _region_get_end(uint32_t index)
{
	return 0;
}

static inline void _region_set_end(uint32_t index, uint32_t end)
{
}

/**
 * This internal function probes the given addr's MPU index.if not
 * in MPU, returns error
 */
static inline int _mpu_probe(uint32_t addr)
{
	return -EINVAL;
}

/**
 * This internal function checks if MPU region is enabled or not
 */
static inline bool _is_enabled_region(uint32_t r_index)
{
	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)
{
	return false;
}
#else /* CONFIG_ARC_NORMAL_FIRMWARE */
/* the following functions are prepared for SECURE_FRIMWARE */
static inline void _region_init(uint32_t index, uint32_t region_addr, uint32_t size,
				uint32_t region_attr)
{
	if (size < (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS)) {
		size = (1 << ARC_FEATURE_MPU_ALIGNMENT_BITS);
	}

	if (region_attr) {
		region_attr &= AUX_MPU_RPER_ATTR_MASK;
		region_attr |=  AUX_MPU_RPER_VALID_MASK;
	}

	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index);
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RSTART, region_addr);
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_REND,
			CALC_REGION_END_ADDR(region_addr, size));
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RPER, region_attr);
}

static inline void _region_set_attr(uint32_t index, uint32_t attr)
{
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index);
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RPER, attr |
				 AUX_MPU_RPER_VALID_MASK);
}

static inline uint32_t _region_get_attr(uint32_t index)
{
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index);

	return z_arc_v2_aux_reg_read(_ARC_V2_MPU_RPER);
}

static inline uint32_t _region_get_start(uint32_t index)
{
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index);

	return z_arc_v2_aux_reg_read(_ARC_V2_MPU_RSTART);
}

static inline void _region_set_start(uint32_t index, uint32_t start)
{
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index);
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_RSTART, start);
}

static inline uint32_t _region_get_end(uint32_t index)
{
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index);

	return z_arc_v2_aux_reg_read(_ARC_V2_MPU_REND) +
		(1 << ARC_FEATURE_MPU_ALIGNMENT_BITS);
}

static inline void _region_set_end(uint32_t index, uint32_t end)
{
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, index);
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_REND, end -
		(1 << ARC_FEATURE_MPU_ALIGNMENT_BITS));
}

/**
 * This internal function probes the given addr's MPU index.if not
 * in MPU, returns error
 */
static inline int _mpu_probe(uint32_t addr)
{
	uint32_t val;

	z_arc_v2_aux_reg_write(_ARC_V2_MPU_PROBE, addr);
	val = z_arc_v2_aux_reg_read(_ARC_V2_MPU_INDEX);

	/* if no match or multiple regions match, return error */
	if (val & 0xC0000000) {
		return -EINVAL;
	} else {
		return val;
	}
}

/**
 * This internal function checks if MPU region is enabled or not
 */
static inline bool _is_enabled_region(uint32_t r_index)
{
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, r_index);
	return ((z_arc_v2_aux_reg_read(_ARC_V2_MPU_RPER) &
		 AUX_MPU_RPER_VALID_MASK) == AUX_MPU_RPER_VALID_MASK);
}

/**
 * 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;

	z_arc_v2_aux_reg_write(_ARC_V2_MPU_INDEX, r_index);
	r_ap = z_arc_v2_aux_reg_read(_ARC_V2_MPU_RPER);
	r_ap &= AUX_MPU_RPER_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 /* CONFIG_ARC_NORMAL_FIRMWARE */

/**
 * This internal function checks the area given by (start, size)
 * and returns the index if the area match one MPU entry
 */
static inline int _get_region_index(uint32_t start, uint32_t size)
{
	int index = _mpu_probe(start);

	if (index > 0 && index == _mpu_probe(start + size - 1)) {
		return index;
	}

	return -EINVAL;
}

#if defined(CONFIG_MPU_GAP_FILLING)
/**
 * This internal function allocates a dynamic MPU region and returns
 * the index or error
 */
static inline int _dynamic_region_allocate_index(void)
{
	if (dynamic_region_index >= get_num_regions()) {
		LOG_ERR("no enough mpu entries %d", dynamic_region_index);
		return -EINVAL;
	}

	return dynamic_region_index++;
}

/* @brief allocate and init a dynamic MPU region
 *
 * This internal function performs the allocation and initialization of
 * a dynamic MPU region
 *
 * @param base region base
 * @param size region size
 * @param attr region attribute
 * @return  <0 failure, >0  allocated dynamic region index
 */
static int _dynamic_region_allocate_and_init(uint32_t base, uint32_t size,
	 uint32_t attr)
{
	int u_region_index = _get_region_index(base, size);
	int region_index;

	LOG_DBG("Region info: base 0x%x size 0x%x attr 0x%x", base, size, attr);

	if (u_region_index == -EINVAL) {
		/* no underlying region */

		region_index = _dynamic_region_allocate_index();

		if (region_index > 0) {
		/* a new region */
			_region_init(region_index, base, size, attr);
		}

		return region_index;
	}

	/*
	 * The new memory region is to be placed inside the underlying
	 * region, possibly splitting the underlying region into two.
	 */

	uint32_t u_region_start = _region_get_start(u_region_index);
	uint32_t u_region_end = _region_get_end(u_region_index);
	uint32_t u_region_attr = _region_get_attr(u_region_index);
	uint32_t end = base + size;


	if ((base == u_region_start) && (end == u_region_end)) {
		/* The new region overlaps entirely with the
		 * underlying region. In this case we simply
		 * update the partition attributes of the
		 * underlying region with those of the new
		 * region.
		 */
		_region_init(u_region_index, base, size, attr);
		region_index = u_region_index;
	} else if (base == u_region_start) {
		/* The new region starts exactly at the start of the
		 * underlying region; the start of the underlying
		 * region needs to be set to the end of the new region.
		 */
		_region_set_start(u_region_index, base + size);
		_region_set_attr(u_region_index, u_region_attr);

		region_index = _dynamic_region_allocate_index();

		if (region_index > 0) {
			_region_init(region_index, base, size, attr);
		}

	} else if (end == u_region_end) {
		/* The new region ends exactly at the end of the
		 * underlying region; the end of the underlying
		 * region needs to be set to the start of the
		 * new region.
		 */
		_region_set_end(u_region_index, base);
		_region_set_attr(u_region_index, u_region_attr);

		region_index = _dynamic_region_allocate_index();

		if (region_index > 0) {
			_region_init(region_index, base, size, attr);
		}

	} else {
		/* The new region lies strictly inside the
		 * underlying region, which needs to split
		 * into two regions.
		 */

		_region_set_end(u_region_index, base);
		_region_set_attr(u_region_index, u_region_attr);

		region_index = _dynamic_region_allocate_index();

		if (region_index > 0) {
			_region_init(region_index, base, size, attr);

			region_index = _dynamic_region_allocate_index();

			if (region_index > 0) {
				_region_init(region_index, base + size,
				 u_region_end - end, u_region_attr);
			}
		}
	}

	return region_index;
}

/* @brief reset the dynamic MPU regions
 *
 * This internal function performs the reset of dynamic MPU regions
 */
static void _mpu_reset_dynamic_regions(void)
{
	uint32_t i;
	uint32_t num_regions = get_num_regions();

	for (i = static_regions_num; i < num_regions; i++) {
		_region_init(i, 0, 0, 0);
	}

	for (i = 0U; i < dynamic_regions_num; i++) {
		_region_init(
			dyn_reg_info[i].index,
			dyn_reg_info[i].base,
			dyn_reg_info[i].size,
			dyn_reg_info[i].attr);
	}

	/* dynamic regions are after static regions */
	dynamic_region_index = static_regions_num;
}

/**
 * @brief configure the base address and size for an MPU region
 *
 * @param   type    MPU region type
 * @param   base    base address in RAM
 * @param   size    size of the region
 */
static inline int _mpu_configure(uint8_t type, uint32_t base, uint32_t size)
{
	uint32_t region_attr = get_region_attr_by_type(type);

	return _dynamic_region_allocate_and_init(base, size, region_attr);
}
#else
/**
 * 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 static_regions_num + THREAD_STACK_REGION;
	case THREAD_STACK_REGION:
	case THREAD_APP_DATA_REGION:
	case THREAD_STACK_GUARD_REGION:
		return static_regions_num + type;
	case THREAD_DOMAIN_PARTITION_REGION:
#if defined(CONFIG_MPU_STACK_GUARD)
		return static_regions_num + type;
#else
		/*
		 * Start domain partition region from stack guard region
		 * since stack guard is not enabled.
		 */
		return static_regions_num + type - 1;
#endif
	default:
		__ASSERT(0, "Unsupported type");
		return -EINVAL;
	}
}

/**
 * @brief configure the base address and size for an MPU region
 *
 * @param   type    MPU region type
 * @param   base    base address in RAM
 * @param   size    size of the region
 */
static inline int _mpu_configure(uint8_t type, uint32_t base, uint32_t size)
{
	int region_index =  get_region_index_by_type(type);
	uint32_t region_attr = get_region_attr_by_type(type);

	LOG_DBG("Region info: 0x%x 0x%x", base, size);

	if (region_attr == 0U || region_index < 0) {
		return -EINVAL;
	}

	_region_init(region_index, base, size, region_attr);

	return 0;
}
#endif

/* ARC Core MPU Driver API Implementation for ARC MPUv3 */

/**
 * @brief enable the MPU
 */
void arc_core_mpu_enable(void)
{
#ifdef CONFIG_ARC_SECURE_FIRMWARE
/* the default region:
 * secure:0x8000, SID:0x10000, KW:0x100 KR:0x80
 */
#define MPU_ENABLE_ATTR	0x18180
#else
#define MPU_ENABLE_ATTR	0
#endif
	arc_core_mpu_default(MPU_ENABLE_ATTR);
}

/**
 * @brief disable the MPU
 */
void arc_core_mpu_disable(void)
{
	/* MPU is always enabled, use default region to
	 * simulate MPU disable
	 */
	arc_core_mpu_default(REGION_ALL_ATTR | AUX_MPU_ATTR_S |
				AUX_MPU_RPER_SID1);
}

/**
 * @brief configure the thread's mpu regions
 *
 * @param thread the target thread
 */
void arc_core_mpu_configure_thread(struct k_thread *thread)
{
#if defined(CONFIG_MPU_GAP_FILLING)
/* the mpu entries of ARC MPUv3 are divided into 2 parts:
 * static entries: global mpu entries, not changed in context switch
 * dynamic entries: MPU entries changed in context switch and
 * memory domain configure, including:
 *    MPU entries for user thread stack
 *    MPU entries for stack guard
 *    MPU entries for mem domain
 *    MPU entries for other thread specific regions
 * before configuring thread specific mpu entries, need to reset dynamic
 * entries
 */
	_mpu_reset_dynamic_regions();
#endif
#if defined(CONFIG_MPU_STACK_GUARD)
	uint32_t guard_start;

	/* Set location of guard area when the thread is running in
	 * supervisor mode. For a supervisor thread, this is just low
	 * memory in the stack buffer. For a user thread, it only runs
	 * in supervisor mode when handling a system call on the privilege
	 * elevation stack.
	 */
#if defined(CONFIG_USERSPACE)
	if ((thread->base.user_options & K_USER) != 0U) {
		guard_start = thread->arch.priv_stack_start;
	} else
#endif
	{
		guard_start = thread->stack_info.start;
	}
	guard_start -= Z_ARC_STACK_GUARD_SIZE;

	if (_mpu_configure(THREAD_STACK_GUARD_REGION, guard_start,
		Z_ARC_STACK_GUARD_SIZE) < 0) {
		LOG_ERR("thread %p's stack guard failed", thread);
		return;
	}
#endif /* CONFIG_MPU_STACK_GUARD */

#if defined(CONFIG_USERSPACE)
	/* configure stack region of user thread */
	if (thread->base.user_options & K_USER) {
		LOG_DBG("configure user thread %p's stack", thread);
		if (_mpu_configure(THREAD_STACK_USER_REGION,
				   (uint32_t)thread->stack_info.start,
				   thread->stack_info.size) < 0) {
			LOG_ERR("thread %p's stack failed", thread);
			return;
		}
	}

#if defined(CONFIG_MPU_GAP_FILLING)
	uint32_t num_partitions;
	struct k_mem_partition *pparts;
	struct k_mem_domain *mem_domain = thread->mem_domain_info.mem_domain;

	/* configure thread's memory domain */
	if (mem_domain) {
		LOG_DBG("configure thread %p's domain: %p",
		 thread, mem_domain);
		num_partitions = mem_domain->num_partitions;
		pparts = mem_domain->partitions;
	} else {
		num_partitions = 0U;
		pparts = NULL;
	}

	for (uint32_t i = 0; i < num_partitions; i++) {
		if (pparts->size) {
			if (_dynamic_region_allocate_and_init(pparts->start,
				pparts->size, pparts->attr) < 0) {
				LOG_ERR(
				"thread %p's mem region: %p failed",
				 thread, pparts);
				return;
			}
		}
		pparts++;
	}
#else
	arc_core_mpu_configure_mem_domain(thread);
#endif
#endif
}

/**
 * @brief configure the default region
 *
 * @param region_attr region attribute of default region
 */
void arc_core_mpu_default(uint32_t region_attr)
{
#ifdef CONFIG_ARC_NORMAL_FIRMWARE
/* \todo through secure service to access mpu */
#else
	z_arc_v2_aux_reg_write(_ARC_V2_MPU_EN, region_attr);
#endif
}

/**
 * @brief configure the MPU region
 *
 * @param index MPU region index
 * @param base  base address
 * @param size  region size
 * @param region_attr region attribute
 */
int arc_core_mpu_region(uint32_t index, uint32_t base, uint32_t size,
			 uint32_t region_attr)
{
	if (index >= get_num_regions()) {
		return -EINVAL;
	}

	region_attr &= AUX_MPU_RPER_ATTR_MASK;

	_region_init(index, base, size, region_attr);

	return 0;
}

#if defined(CONFIG_USERSPACE)
/**
 * @brief configure MPU regions for the memory partitions of the memory domain
 *
 * @param thread the thread which has memory domain
 */
#if defined(CONFIG_MPU_GAP_FILLING)
void arc_core_mpu_configure_mem_domain(struct k_thread *thread)
{
	arc_core_mpu_configure_thread(thread);
}
#else
void arc_core_mpu_configure_mem_domain(struct k_thread *thread)
{
	uint32_t region_index;
	uint32_t num_partitions;
	uint32_t num_regions;
	struct k_mem_partition *pparts;
	struct k_mem_domain *mem_domain = NULL;

	if (thread) {
		mem_domain = thread->mem_domain_info.mem_domain;
	}

	if (mem_domain) {
		LOG_DBG("configure domain: %p", mem_domain);
		num_partitions = mem_domain->num_partitions;
		pparts = mem_domain->partitions;
	} else {
		LOG_DBG("disable domain partition regions");
		num_partitions = 0U;
		pparts = NULL;
	}

	num_regions = get_num_regions();
	region_index = get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION);

	while (num_partitions && region_index < num_regions) {
		if (pparts->size > 0) {
			LOG_DBG("set region 0x%x 0x%lx 0x%x",
				region_index, pparts->start, pparts->size);
			_region_init(region_index, pparts->start,
				     pparts->size, pparts->attr);
			region_index++;
		}
		pparts++;
		num_partitions--;
	}

	while (region_index < num_regions) {
		/* clear the left mpu entries */
		_region_init(region_index, 0, 0, 0);
		region_index++;
	}
}
#endif

/**
 * @brief remove MPU regions for the memory partitions of the memory domain
 *
 * @param mem_domain the target memory domain
 */
void arc_core_mpu_remove_mem_domain(struct k_mem_domain *mem_domain)
{
	uint32_t num_partitions;
	struct k_mem_partition *pparts;
	int index;

	if (mem_domain) {
		LOG_DBG("configure domain: %p", mem_domain);
		num_partitions = mem_domain->num_partitions;
		pparts = mem_domain->partitions;
	} else {
		LOG_DBG("disable domain partition regions");
		num_partitions = 0U;
		pparts = NULL;
	}

	for (uint32_t i = 0; i < num_partitions; i++) {
		if (pparts->size) {
			index = _get_region_index(pparts->start,
			 pparts->size);
			if (index > 0) {
#if defined(CONFIG_MPU_GAP_FILLING)
				_region_set_attr(index,
				REGION_KERNEL_RAM_ATTR);
#else
				_region_init(index, 0, 0, 0);
#endif
			}
		}
		pparts++;
	}
}

/**
 * @brief reset MPU region for a single memory partition
 *
 * @param partition_id  memory partition id
 */
void arc_core_mpu_remove_mem_partition(struct k_mem_domain *domain,
			uint32_t partition_id)
{
	struct k_mem_partition *partition = &domain->partitions[partition_id];

	int region_index = _get_region_index(partition->start,
			 partition->size);

	if (region_index < 0) {
		return;
	}

	LOG_DBG("remove region 0x%x", region_index);
#if defined(CONFIG_MPU_GAP_FILLING)
	_region_set_attr(region_index, REGION_KERNEL_RAM_ATTR);
#else
	_region_init(region_index, 0, 0, 0);
#endif
}

/**
 * @brief get the maximum number of free regions for memory domain partitions
 */
int arc_core_mpu_get_max_domain_partition_regions(void)
{
#if defined(CONFIG_MPU_GAP_FILLING)
	/* consider the worst case: each partition requires split */
	return (get_num_regions() - MPU_REGION_NUM_FOR_THREAD) / 2;
#else
	return get_num_regions() -
	       get_region_index_by_type(THREAD_DOMAIN_PARTITION_REGION) - 1;
#endif
}

/**
 * @brief validate the given buffer is user accessible or not
 */
int arc_core_mpu_buffer_validate(void *addr, size_t size, int write)
{
	int r_index;
	int key = arch_irq_lock();

	/*
	 * For ARC MPU v3, overlapping is not supported.
	 * we can stop the iteration immediately once we find the
	 * matched region that grants permission or denies access.
	 */
	r_index = _mpu_probe((uint32_t)addr);
	/*  match and the area is in one region */
	if (r_index >= 0 && r_index == _mpu_probe((uint32_t)addr + (size - 1))) {
		if (_is_user_accessible_region(r_index, write)) {
			r_index = 0;
		} else {
			r_index = -EPERM;
		}
	} else {
		r_index = -EPERM;
	}

	arch_irq_unlock(key);

	return r_index;
}
#endif /* CONFIG_USERSPACE */

/* ARC MPU Driver Initial Setup */
/*
 * @brief MPU default initialization and configuration
 *
 * This function provides the default configuration mechanism for the Memory
 * Protection Unit (MPU).
 */
static int arc_mpu_init(const struct device *arg)
{
	ARG_UNUSED(arg);
	uint32_t num_regions;
	uint32_t i;

	num_regions = get_num_regions();

	/* ARC MPU supports up to 16 Regions */
	if (mpu_config.num_regions > num_regions) {
		__ASSERT(0,
		"Request to configure: %u regions (supported: %u)\n",
		mpu_config.num_regions, num_regions);
		return -EINVAL;
	}

	static_regions_num = 0;

	/* Disable MPU */
	arc_core_mpu_disable();

	for (i = 0U; i < mpu_config.num_regions; i++) {
		/* skip empty region */
		if (mpu_config.mpu_regions[i].size == 0) {
			continue;
		}
#if defined(CONFIG_MPU_GAP_FILLING)
		_region_init(static_regions_num,
			     mpu_config.mpu_regions[i].base,
			     mpu_config.mpu_regions[i].size,
			     mpu_config.mpu_regions[i].attr);

		/* record the static region which can be split */
		if (mpu_config.mpu_regions[i].attr & REGION_DYNAMIC) {
			if (dynamic_regions_num >=
			MPU_DYNAMIC_REGION_AREAS_NUM) {
				LOG_ERR("not enough dynamic regions %d",
				 dynamic_regions_num);
				return -EINVAL;
			}

			dyn_reg_info[dynamic_regions_num].index = i;
			dyn_reg_info[dynamic_regions_num].base =
				mpu_config.mpu_regions[i].base;
			dyn_reg_info[dynamic_regions_num].size =
				mpu_config.mpu_regions[i].size;
			dyn_reg_info[dynamic_regions_num].attr =
				mpu_config.mpu_regions[i].attr;

			dynamic_regions_num++;
		}
		static_regions_num++;
#else
		/* dynamic region will be covered by default mpu setting
		 * no need to configure
		 */
		if (!(mpu_config.mpu_regions[i].attr & REGION_DYNAMIC)) {
			_region_init(static_regions_num,
			     mpu_config.mpu_regions[i].base,
			     mpu_config.mpu_regions[i].size,
			     mpu_config.mpu_regions[i].attr);
			static_regions_num++;
		}
#endif
	}

	for (i = static_regions_num; i < num_regions; i++) {
		_region_init(i, 0, 0, 0);
	}

	/* Enable MPU */
	arc_core_mpu_enable();

	return 0;
}

SYS_INIT(arc_mpu_init, PRE_KERNEL_1,
	 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

#endif /* ZEPHYR_ARCH_ARC_CORE_MPU_ARC_MPU_V3_INTERNAL_H_ */