Skip to content

Add Cortex-R support #2261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boot/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ config BOOT_SIGNATURE_KEY_FILE

config MCUBOOT_CLEANUP_ARM_CORE
bool "Perform core cleanup before chain-load the application"
depends on CPU_CORTEX_M
depends on CPU_CORTEX_M || CPU_CORTEX_R5
default y
help
This option instructs MCUboot to perform a clean-up of a set of
Expand Down
61 changes: 60 additions & 1 deletion boot/zephyr/arm_cleanup.c
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2025 Siemens Mobility GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdint.h>
#include <zephyr/irq.h>
#include "zephyr/sys/util_macro.h"
#include <zephyr/toolchain.h>

#ifdef CONFIG_CPU_CORTEX_M
#include <cmsis_core.h>
#endif
#if CONFIG_CPU_HAS_NXP_MPU
#include <fsl_sysmpu.h>
#endif

void cleanup_arm_nvic(void) {
#ifndef CONFIG_CPU_CORTEX_M

#ifdef CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER
extern void z_soc_irq_eoi(unsigned int irq);
#else
#include <zephyr/drivers/interrupt_controller/gic.h>
#endif

#endif /* CONFIG_CPU_CORTEX_M */

void cleanup_arm_interrupts(void) {
/* Allow any pending interrupts to be recognized */
__ISB();
__disable_irq();

#ifdef CONFIG_CPU_CORTEX_M
/* Disable NVIC interrupts */
for (uint8_t i = 0; i < ARRAY_SIZE(NVIC->ICER); i++) {
NVIC->ICER[i] = 0xFFFFFFFF;
Expand All @@ -24,11 +41,27 @@ void cleanup_arm_nvic(void) {
for (uint8_t i = 0; i < ARRAY_SIZE(NVIC->ICPR); i++) {
NVIC->ICPR[i] = 0xFFFFFFFF;
}
#else

for (unsigned int i = 0; i < CONFIG_NUM_IRQS; ++i) {
irq_disable(i);
}

for (unsigned int i = 0; i < CONFIG_NUM_IRQS; ++i) {
#ifdef CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER
z_soc_irq_eoi(i);
#else
arm_gic_eoi(i);
#endif /* CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER */
}

#endif /* CONFIG_CPU_CORTEX_M */
}

#if CONFIG_CPU_HAS_ARM_MPU
__weak void z_arm_clear_arm_mpu_config(void)
{
#ifdef CONFIG_CPU_CORTEX_M
int i;

int num_regions =
Expand All @@ -37,6 +70,32 @@ __weak void z_arm_clear_arm_mpu_config(void)
for (i = 0; i < num_regions; i++) {
ARM_MPU_ClrRegion(i);
}
#else
uint8_t i;
uint8_t num_regions;
uint32_t mpu_type_register;

// disable MPU
__asm__(
" mrc p15, 0, r0, c1, c0, 0\n"
" bic r0, #1\n"
" mcr p15, 0, r0, c1, c0, 0\n"
" isb\n"
::: "r0");

// the number of MPU regions is stored in bits 15:8 of the MPU type register
__asm__ volatile("mrc p15, 0, %0, c0, c0, 4\n" : "=r" (mpu_type_register) ::);
num_regions = (uint8_t) ((mpu_type_register >> 8) & BIT_MASK(8));

for (i = 0; i < num_regions; ++i) {
// select region in the MPU and clear the region size field
__asm__ volatile(
" mov r0, #0\n"
" mcr p15, 0, %0, c6, c2, 0\n"
" mcr p15, 0, r0, c6, c1, 2\n"
:: "r" (i) : "r0");
}
Comment on lines +74 to +97
Copy link
Collaborator

@nordicjm nordicjm Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be using zephyr APIs, should not be adding code, let alone assembly, here to do this (applies to clearing MPU, the boot assembly code is fine)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the inline assmebly isn't really good here. I was also thinking about using Zephyr functions but then noticed that arm_core_mpu_disable is not public (as in: Not in any header) and only interally used, meaning it could change.

For disabling (clearing) the individual regions Zephyr has a non-public header that provides ARM_MPU_ClrRegion (link), which is the same name CMSIS provides only for Cortex-M cores.

#endif /* CONFIG_CPU_CORTEX_M */
}
#elif CONFIG_CPU_HAS_NXP_MPU
__weak void z_arm_clear_arm_mpu_config(void)
Expand Down
2 changes: 1 addition & 1 deletion boot/zephyr/include/arm_cleanup.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/**
* Cleanup interrupt priority and interupt enable registers.
*/
void cleanup_arm_nvic(void);
void cleanup_arm_interrupts(void);

#if defined(CONFIG_CPU_HAS_ARM_MPU) || defined(CONFIG_CPU_HAS_NXP_MPU)
/**
Expand Down
47 changes: 45 additions & 2 deletions boot/zephyr/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (c) 2012-2014 Wind River Systems, Inc.
* Copyright (c) 2020 Arm Limited
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
* Copyright (c) 2025 Siemens Mobility GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,7 +32,7 @@
#include <zephyr/cache.h>
#endif

#if defined(CONFIG_ARM)
#if defined(CONFIG_CPU_CORTEX_M)
#include <cmsis_core.h>
#endif

Expand Down Expand Up @@ -141,8 +142,19 @@ extern void *_vector_table_pointer;
#endif

struct arm_vector_table {
#ifdef CONFIG_CPU_CORTEX_M
uint32_t msp;
uint32_t reset;
#else
uint32_t reset;
uint32_t undef_instruction;
uint32_t svc;
uint32_t abort_prefetch;
uint32_t abort_data;
uint32_t reserved;
uint32_t irq;
uint32_t fiq;
#endif
};

static void do_boot(struct boot_rsp *rsp)
Expand Down Expand Up @@ -189,7 +201,7 @@ static void do_boot(struct boot_rsp *rsp)
usb_disable();
#endif
#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE
cleanup_arm_nvic(); /* cleanup NVIC registers */
cleanup_arm_interrupts(); /* disable and acknowledge all interrupts */

#if defined(CONFIG_BOOT_DISABLE_CACHES)
/* Flush and disable instruction/data caches before chain-loading the application */
Expand Down Expand Up @@ -232,10 +244,31 @@ static void do_boot(struct boot_rsp *rsp)
#endif
#endif /* CONFIG_BOOT_INTR_VEC_RELOC */

#ifdef CONFIG_CPU_CORTEX_M
__set_MSP(vt->msp);
#endif

#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE
#ifdef CONFIG_CPU_CORTEX_M
__set_CONTROL(0x00); /* application will configures core on its own */
__ISB();
#else
/* Set mode to supervisor and A, I and F bit as described in the
* Cortex R5 TRM */
__asm__ volatile(
" mrs r0, CPSR\n"
/* change mode bits to supervisor */
" bic r0, #0x1f\n"
" orr r0, #0x13\n"
/* set the A, I and F bit */
" mov r1, #0b111\n"
" lsl r1, #0x6\n"
" orr r0, r1\n"

" msr CPSR, r0\n"
::: "r0", "r1");
#endif /* CONFIG_CPU_CORTEX_M */

#endif
#if CONFIG_MCUBOOT_CLEANUP_RAM
__asm__ volatile (
Expand Down Expand Up @@ -263,7 +296,17 @@ static void do_boot(struct boot_rsp *rsp)
: "r0", "r1", "r2", "r3", "memory"
);
#else

#ifdef CONFIG_CPU_CORTEX_M
((void (*)(void))vt->reset)();
#else
/* Some architectures like the Cortex-R run in thumb mode but reset into ARM
* mode. To do the switch into ARM mode an explicit branch with exchange
* instruction set is needed
*/
__asm__("bx %0\n" : : "r" (&vt->reset));
#endif

#endif
}

Expand Down
1 change: 1 addition & 0 deletions docs/release-notes.d/add-cortex-r-support-00-basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added support for booting Cortex-R5 images
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add support for cleaning up the Cortex-R core before final jumping