Linux on Cortex-M3
Introduction
Cortex-M3 is an ARMv7-M CPU targeting the microcontrollers space. It
supports the Thumb-2 instruction set, Memory Protection Unit (MPU but
no MMU), integrated Nested Vectored Interrupt Controller (NVIC),
timer.
Cortex-M3 supports two operating modes - Thread and Handler. The
Thread mode can be privileged or unprivileged. The Handler mode is
always privileged. At reset, the CPU starts in privileged Thread mode
and switching to the Handler mode is done via an interrupt, exception
or an explicit system call (the SVC instruction).
Cortex-M3 has two stacks - the main stack and the process stack. The
Thread mode can use either the main or the process stacks. The Handler
mode always uses the main stack. When taking an exception, the CPU
automatically saves the state (R0-R3, R12, LR, ReturnAddress, xPSR) on
the main stack. When returning from an exception, simply loading the
exception LR into PC causes the state previously saved on the stack to
be restored. Note that interrupts are not automatically disabled or
enabled on exception entry or exit.
For more information, see the Cortex-M3 Technical Reference Manual and
the ARMv7-M Architecture Reference Manual on the
http://infocenter.arm.com/ website.
Getting started
Source code
The Cortex-M3 Linux port is hosted on the Git tree at:
http://www.linux-arm.org/git?p=linux-2.6.git;a=shortlog;h=cortex-m3
The current version is based on the 2.6.26-rc8 kernel. The full source
code snapshot (Linux 2.6.26-rc8 plus patches enabling the Cortex-M3
support) can be downloaded from:
http://www.linux-arm.org/git?p=linux-2.6.git;a=snapshot;h=cortex-m3
The full patch (including uClinux fixes, Thumb-2 and Cortex-M3
support) can be downloaded from:
http://www.linux-arm.org/git?p=linux-2.6.git;a=commitdiff_plain;h=cortex-m3;hp=v2.6.26-rc8
The port currently supports Cortex-M3 hosted in the FPGA of RealView
EB.
The filesystem consists of a cut-down version of busybox with minimal
functionality. A more complex Thumb-2 filesystem can be used if
sufficient RAM is available on the board. Busybox 1.10.1 (newer
version can be used) is available from:
http://busybox.net/downloads/busybox-1.10.1.tar.bz2
Building the Linux kernel
For building the Linux kernel, a recent toolchain with support for
ARMv7-M is required. One can be downloaded from CodeSourcery's website:
http://www.codesourcery.com/gnu_toolchains/arm/portal/package1490/public/arm-none-linux-gnueabi/arm-2007q1-21-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
To build the kernel, follow the steps below:
- Using the source code snapshot mentioned above, copy the attached .config and initramfs-list-min files in the top directory of the kernel tree.
- Modify the CONFIG_INITRAMFS_SOURCE variable in the .config file to point to the initramfs-list-min file
- Modify the initramfs-list-min file to point to the built busybox tool (see below for details about building busybox)
- Build the kernel (do not build the compressed image if the amount of RAM is small):
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- Image
The Image is available in the arch/arm/boot/ directory.
Building busybox
A
Pre-built busybox file is attached to this page.
For building busybox (or other applications), a compiler supporting uClinux and ARMv7-M is needed. One can be downloaded from CodeSourcery's website:
http://www.codesourcery.com/gnu_toolchains/arm/portal/package1496/public/arm-uclinuxeabi/arm-2007q1-21-arm-uclinuxeabi-i686-pc-linux-gnu.tar.bz2
In the extracted busybox source directory copy the attached
.config
file and create the arch/arm/Makefile file containing the compiler
details:
CROSS_COMPILE = arm-uclinuxeabi-
CFLAGS += -march=armv7m -mthumb -Wl,-elf2flt=-s -Wl,-elf2flt=16384
To modify the configuration:
make ARCH=arm menuconfig
To compile busybox:
make ARCH=arm
The busybox file is created in the top directory. Use the path to the
built busybox in the initramfs-list-min file.
Running Linux on RealView EB + Cortex-M3
The current method for running Linux on RealView EB + Cortex-M3 is to
load the Image file with RVD at 0x20008000 and set the appropriate
registers. Because there is no boot monitor to initialise the board,
the CPU is initially stuch in a Hard Fault. The following commands can
be copied to an RVD script to run Linux:
;; Ensure the Thumb mode and no current exception
setreg @XPSR = 0x01000000
;; Reset any faults (Application Interrupt and Reset Control Register)
setmem/w 0xe000ed0c = 0x05fa0002
setmem/w 0xe000ed28 = 0xffffffff
;; UART setup - RealView EB
;; ibrd - 38400 (24MHz clock)
setmem/w 0x40009000 + 0x24 = 39
;; fbrd
setmem/w 0x40009000 + 0x28 = 4
;; cr_h - 8N1
setmem/w 0x40009000 + 0x2c = 0x60
;; cr - hardware flow control
setmem/w 0x40009000 + 0x30 = 0x0301
readfile,raw "...../arch/arm/boot/Image"=0x20008000
setreg @R0 = 0
setreg @R1 = 827
setreg @R2 = 0
setreg @R3 = 0
setreg @PC = 0x20008000
;; clear the first page
fill 0x20000000..0x20000fff = 0
;; ATAG_CORE
setmem/w 0x20000100 = 2
setmem/w 0x20000104 = 0x54410001
;; ATAG_CMDLINE (size (1024 + 8) / 4)
setmem/w 0x20000108 = 258
setmem/w 0x2000010c = 0x54410009
;; command line
setmem/b 0x20000110 = "init=/bin/sh console=ttyAMA0 mem=2M"
go
Details of the Linux port
Prerequisites
The Linux kernel used as a base for Cortex-M3 must support MMU-less
CPUs and be compilable to the Thumb-2 instruction set. Patches
supporting this are already implemented and available via the above
links.
Base support
The base patch for Cortex-M3 adds the corresponding arch/arm/mm/ files
and several #ifdef's around the kernel for cases where the Cortex-M3
functionality is different (no CP15 register, different xPSR etc.).
The register definitions in include/asm-arm/ptrace.h were reordered to
benefit from the automatic state saving on exception
entry.
ARM_ORIG_r0 was removed and
ARM_EXC_lr added to hold the value
of the exception LR register (which has a different meaning from the
ReturnAddress).
Linux initially boots in the privileged Thread mode but it is switched
to Handler mode in proc-v7m.S via an SVCall during the boot
process. After this, the kernel start-up code and kernel threads run
in Handler mode (using the main stack). User-space processes run in
unprivileged Thread mode using the process stack.
Linux requires two stack for a user-space thread - a kernel stack
(main stack on Cortex-M3) and a user stack (process stack on
Cortex-M3). Kernel threads only require one stack (main stack on
Cortex-M3). Context switching is performed by switching the kernel
stacks corresponding to the current and new thread. If the
scheduled-in thread is a user thread, the user stack is automatically
activated and the state restored when returning from kernel (returning
from Handler to Thread mode).
Exception handling
To simplify the exception handling mechanism, the decision was made to
run kernel threads in Handler mode with SVCall priority. The
disadvantage of this approach is that kernel preemption is not
possible since the Handler mode cannot be preempted by the Thread
mode. Alternatively, the exception handling code can be improved so
that it only runs for a short time and switches to the privileged
Thread mode for executing the rest of the kernel code. ARMv7-M allows
the interrupts to remain disabled when returning from an exception
handler. The exception handling code would become a micro-scheduler
responsible for switching between kernel and user applications and
passing arguments between the two.
The main exception code is implemented in the
arch/arm/kernel/entry-v7m.S file. It contains the exception vector
table together with handlers for interrupts and PendSV (other
exceptions are, for now, considered fatal and not handled). This file
also contains the
__switch_to function responsible for context
switching between two threads.
The complete register state saving and restoring is handled by the
v7m_exception_entry,
v7m_exception_fast_exit and
v7m_exception_slow_exit macros defined in entry-header.S. The
fast_exit macro is used when returning from an interrupt handler while
the slow_exit one is used when returning from a system call or
PendSV. The interrupted mode is detected from the value of the
exception LR register. This is especially important when returning to
user space for setting the correct process stack.
Context switching can only occur during SVCall or PendSV
handling. These exceptions have the same priority but lower than
interrupts to allow IRQ handling during system call processing. If the
current task needs to be rescheduled after an interrupt handling, the
__irq_entry code raises a PendSV which will be taken after the current
handler completes.
Nested Vectored Interrupt Controller
The NVIC support is pretty similar to the GIC one. The patch adds the
standard Linux functions for masking, unmasking and acknowledging
interrupts. The IRQ number that generated an interrupt is read from
the IPSR register in the
__irq_entry code in entry-v7m.S.
RealView EB + Cortex-M3
The Cortex-M3 hosted in the FPGA of RealView EB has a different memory
map from standard EB board. The patch modifies the
include/asm-arm/arch-realview/board-eb.h and platform.h files
accordingly and adds the NVIC initialisation call.
Attachments
- .config: Linux on Cortex-M3 configuration file
- busybox: Pre-built busybox for ARMv7-M
--
CatalinMarinas - 01 Jul 2008