Skip to content

Instantly share code, notes, and snippets.

@xor-gate
Last active March 11, 2024 12:05
Show Gist options
  • Save xor-gate/91a506ef0127bca97aab to your computer and use it in GitHub Desktop.
Save xor-gate/91a506ef0127bca97aab to your computer and use it in GitHub Desktop.
Semhosting

QEMU Cortex-m3 with semihosting

cmake_minimum_required(VERSION 3.0)
project(semihosting)
set(SRC main.c semihosting.c startup.c)
add_executable(main ${SRC})
SET_TARGET_PROPERTIES(main PROPERTIES LINK_FLAGS "-T ${CMAKE_SOURCE_DIR}/standalone.ld -nostartfiles -Wl,--gc-sections")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T ${CMAKE_SOURCE_DIR}/standalone.ld -L ${CMAKE_SOURCE_DIR}" )
#include <limits.h>
#include <stdatomic.h>
#include "semihosting.h"
int main()
{
int fd;
static atomic_flag cat = ATOMIC_FLAG_INIT;
static unsigned volatile counter= 0;
do {} while (atomic_flag_test_and_set(&cat));
++counter;
atomic_flag_clear(&cat);
fd = semihosting_open("./semihosting.log");
semihosting_write(fd, "Hello World!\n", sizeof("Hello World!\n"));
while (1) {
semihosting_write(1, "Hello World!\n", sizeof("Hello World!\n"));
for (int i = 0; i < 10000000; i++)
__asm__("nop");
}
}
# From: http://shukra.cedt.iisc.ernet.in/w/index.php?title=EmSys:Starting_Cortex-M3_Development_Using_the_GNU_Tool_Chain_-_Part_2
all:
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -g -c semihosting.c -o semihosting.o
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -std=c11 -g -c main.c -o main.o
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -g -c startup.c -o startup.o
arm-none-eabi-ld -g -T standalone.ld semihosting.o startup.o main.o -o main.elf
arm-none-eabi-objcopy -O binary main.elf main.bin
qemu-system-arm -M lm3s6965evb --kernel main.bin --serial null -nographic -monitor null -semihosting
# target remote localhost:1234
objdump:
arm-none-eabi-objdump -d ./main.elf
gdb:
arm-none-eabi-gdb main.elf
clean:
rm -f *.o *.elf *.bin *.log
#include <string.h>
#include <stdint.h>
#include "semihosting.h"
void *__semihosting(uint32_t op, void *arg)
{
(void)op;
(void)arg;
#ifdef __thumb2__
asm("push {LR}; bkpt 0xAB; pop {PC}");
#else
asm("push {LR}; svc 0xAB; pop {PC}");
#endif
}
int semihosting_open(void *name)
{
uint32_t open_parms[3];
int status;
open_parms[0] = (uint32_t)name;
open_parms[1] = 9;
// open_parms[2] = strlen(name);
status = (int)__semihosting(SYS_OPEN, open_parms);
if (status < 0)
return -1;
return status;
}
int semihosting_close(unsigned int fd)
{
uint32_t close_parms[1];
int status;
close_parms[0] = fd;
status = (int)__semihosting(SYS_CLOSE, close_parms);
if (status < 0)
return -1;
return 0;
}
int semihosting_read(unsigned int fd, void *buf, unsigned int count)
{
uint32_t read_parms[3];
int status;
read_parms[0] = fd;
read_parms[1] = (uint32_t)buf;
read_parms[2] = count;
status = (int)__semihosting(SYS_READ, read_parms);
if (status < 0)
return status;
return count - status;
}
int semihosting_write(unsigned int fd, const void *buf, unsigned int count)
{
uint32_t write_parms[3];
int status;
write_parms[0] = fd;
write_parms[1] = (uint32_t)buf;
write_parms[2] = count;
status = (int)__semihosting(SYS_WRITE, write_parms);
if (status < 0)
return status;
return count - status;
}
/* ADP_Stopped_ApplicationExit is used for exit(0),
* anything else is implemented as exit(1) */
//http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0473c/CEGFDGED.html
#define ADP_Stopped_ApplicationExit (0x20026)
void semihosting_exit(int ret)
{
uint32_t write_parms[1];
int status;
if (ret == 0)
ret = ADP_Stopped_ApplicationExit;
write_parms[0] = ret;
__semihosting(SYS_EXIT, write_parms);
}
// Semi-hosting I/O services
// $Id$
// Copyright (C)2013-2015, Philip Munts, President, Munts AM Corp.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// These services ONLY work in debug mode. If your program calls any of these,
// it will NOT work unless you are debugging.
// Be sure to issue "monitor arm semihosting enable" in gdb before starting
// your program.
// Semi-hosting operations -- From "ARM Compiler toolchain Developing
// Software for ARM Processors,Version 5.03"
#include <stdint.h>
#define SYS_OPEN 0x01
#define SYS_CLOSE 0x02
#define SYS_WRITEC 0x03
#define SYS_WRITE0 0x04
#define SYS_WRITE 0x05
#define SYS_READ 0x06
#define SYS_READC 0x07
#define SYS_ISERROR 0x08
#define SYS_ISTTY 0x09
#define SYS_SEEK 0x0A
#define SYS_FLEN 0x0C
#define SYS_TMPNAM 0x0D
#define SYS_REMOVE 0x0E
#define SYS_CLOCK 0x10
#define SYS_TIME 0x11
#define SYS_SYSTEM 0x12
#define SYS_ERRNO 0x13
#define SYS_GET_CMDLINE 0x15
#define SYS_HEAPINFO 0x16
#define SYS_EXIT 0x18
#define SYS_ELAPSED 0x30
#define SYS_TICKFREQ 0x31
// Function prototypes
void *__semihosting(uint32_t op, void *arg) __attribute__((naked));
int semihosting_register(char *name);
int semihosting_stdio(char *name);
int semihosting_open(void *name);
int semihosting_close(unsigned int fd);
int semihosting_read(unsigned int fd, void *buf, unsigned int count);
int semihosting_write(unsigned int fd, const void *buf, unsigned int count);
int semihosting_read_ready(unsigned int fd);
int semihosting_write_ready(unsigned int fd);
void semihosting_exit(int ret);
ENTRY(ResetISR)
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
.text :
{
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
.data : AT (ADDR(.text) + SIZEOF(.text))
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM
.bss :
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > SRAM
}
void ResetISR(void);
static void NmiSR(void);
static void FaultISR(void);
static void IntDefaultHandler(void);
extern int main(void);
static unsigned long pulStack[64];
//*****************************************************************************
//
// The vector table. Note that the proper constructs must be placed on this to
// ensure that it ends up at physical address 0x0000.0000.
//
//*****************************************************************************
__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) =
{
(void (*)(void))((unsigned long)pulStack + sizeof(pulStack)),
// The initial stack pointer
ResetISR, // The reset handler
NmiSR, // The NMI handler
FaultISR, // The hard fault handler
IntDefaultHandler, // The MPU fault handler
IntDefaultHandler, // The bus fault handler
IntDefaultHandler, // The usage fault handler
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
IntDefaultHandler, // SVCall handler
IntDefaultHandler, // Debug monitor handler
0, // Reserved
IntDefaultHandler, // The PendSV handler
IntDefaultHandler // The SysTick handler
};
//*****************************************************************************
//
// The following are constructs created by the linker, indicating where the
// the "data" and "bss" segments reside in memory. The initializers for the
// "data" segment resides immediately following the "text" segment.
//
//*****************************************************************************
extern unsigned long _etext;
extern unsigned long _data;
extern unsigned long _edata;
extern unsigned long _bss;
extern unsigned long _ebss;
//*****************************************************************************
//
// This is the code that gets called when the processor first starts execution
// following a reset event. Only the absolutely necessary set is performed,
// after which the application supplied entry() routine is called.
//
//*****************************************************************************
void ResetISR(void)
{
unsigned long *pulSrc, *pulDest;
//
// Copy the data segment initializers from flash to SRAM.
//
pulSrc = &_etext;
pulDest = &_data;
while(pulDest < &_edata )
{
*pulDest++ = *pulSrc++;
}
//
// Zero fill the bss segment.
//
__asm(" ldr r0, =_bss\n"
" ldr r1, =_ebss\n"
" mov r2, #0\n"
" .thumb_func\n"
"zero_loop:\n"
" cmp r0, r1\n"
" it lt\n"
" strlt r2, [r0], #4\n"
" blt zero_loop");
//
// Call the application's entry point.
//
main();
}
//*****************************************************************************
//
// This is the code that gets called when the processor receives a NMI. This
// simply enters an infinite loop, preserving the system state for examination
// by a debugger.
//
//*****************************************************************************
static void NmiSR(void)
{
//
// Enter an infinite loop.
//
while(1) {
;
}
}
//*****************************************************************************
//
// This is the code that gets called when the processor receives a fault
// interrupt. This simply enters an infinite loop, preserving the system state
// for examination by a debugger.
//
//*****************************************************************************
static void FaultISR(void)
{
//
// Enter an infinite loop.
//
while(1) {
;
}
}
//*****************************************************************************
//
// This is the code that gets called when the processor receives an unexpected
// interrupt. This simply enters an infinite loop, preserving the system state
// for examination by a debugger.
//
//*****************************************************************************
static void IntDefaultHandler(void)
{
//
// Go into an infinite loop.
//
while(1) {
;
}
}
# CMake Toolchain file for the gcc-arm-embedded toolchain.
# https://launchpad.net/gcc-arm-embedded
#
# Copyright (c) 2013 Swift Navigation Inc.
# Contact: Fergus Noble <[email protected]>
#
# This source is subject to the license found in the file 'LICENSE' which must
# be be distributed together with this source. All other rights reserved.
#
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
include(CMakeForceCompiler)
# Targeting an embedded system, no OS.
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR cortex-m3)
CMAKE_FORCE_C_COMPILER(arm-none-eabi-gcc GNU)
CMAKE_FORCE_CXX_COMPILER(arm-none-eabi-g++ GNU)
# Find the target environment prefix..
# First see where gcc is keeping libc.a
execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.a
OUTPUT_VARIABLE CMAKE_INSTALL_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Strip the filename off
get_filename_component(CMAKE_INSTALL_PREFIX
"${CMAKE_INSTALL_PREFIX}" PATH
)
# Then find the canonical path to the directory one up from there
get_filename_component(CMAKE_INSTALL_PREFIX
"${CMAKE_INSTALL_PREFIX}/.." REALPATH
)
set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE FILEPATH
"Install path prefix, prepended onto install directories.")
message(STATUS "Cross-compiling with the gcc-arm-embedded toolchain")
message(STATUS "Toolchain prefix: ${CMAKE_INSTALL_PREFIX}")
set(CMAKE_FIND_ROOT_PATH ${CMAKE_INSTALL_PREFIX})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS}"
"-fno-common -ffunction-sections -fdata-sections"
)
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "cortex-m4")
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS}"
"-mcpu=cortex-m4 -march=armv7e-m -mthumb"
"-mfloat-abi=hard -mfpu=fpv4-sp-d16"
)
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "cortex-m3")
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS}"
"-mcpu=cortex-m3 -march=armv7-m -mthumb "
"-msoft-float"
)
# --specs=nosys.specs
else ()
message(WARNING
"Processor not recognised in toolchain file, "
"compiler flags not configured."
)
endif ()
# When we break up long strings in CMake we get semicolon
# separated lists, undo this here...
string(REGEX REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "")
set(BUILD_SHARED_LIBS OFF)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment