mirror of https://github.com/xemu-project/xemu.git
tests/tcg: enable arm softmmu tests
To make it easier to test 32 bit Arm softmmu issues implement a basic boot.S so we can build the multiarch tests. Currently CHECK_UNALIGNED is disabled as I haven't got the right magic set for it to work. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20231120150833.2552739-12-alex.bennee@linaro.org>
This commit is contained in:
parent
e8368b1c95
commit
56611e17d2
|
@ -8,20 +8,64 @@ ARM_SRC=$(SRC_PATH)/tests/tcg/arm/system
|
||||||
# Set search path for all sources
|
# Set search path for all sources
|
||||||
VPATH += $(ARM_SRC)
|
VPATH += $(ARM_SRC)
|
||||||
|
|
||||||
ARM_TESTS=test-armv6m-undef
|
|
||||||
|
|
||||||
TESTS += $(ARM_TESTS)
|
|
||||||
|
|
||||||
LDFLAGS+=-nostdlib -N -static
|
|
||||||
|
|
||||||
%: %.S %.ld
|
|
||||||
$(CC) $(CFLAGS) $(ASFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -T $(ARM_SRC)/$@.ld
|
|
||||||
|
|
||||||
# Specific Test Rules
|
# Specific Test Rules
|
||||||
|
|
||||||
test-armv6m-undef: EXTRA_CFLAGS+=-mcpu=cortex-m0 -mfloat-abi=soft -Wl,--build-id=none -x assembler-with-cpp
|
test-armv6m-undef: test-armv6m-undef.S
|
||||||
|
$(CC) -mcpu=cortex-m0 -mfloat-abi=soft \
|
||||||
|
-Wl,--build-id=none -x assembler-with-cpp \
|
||||||
|
$< -o $@ -nostdlib -N -static \
|
||||||
|
-T $(ARM_SRC)/$@.ld
|
||||||
|
|
||||||
run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel
|
run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel
|
||||||
|
|
||||||
# We don't currently support the multiarch system tests
|
ARM_TESTS+=test-armv6m-undef
|
||||||
undefine MULTIARCH_TESTS
|
|
||||||
|
# These objects provide the basic boot code and helper functions for all tests
|
||||||
|
CRT_OBJS=boot.o
|
||||||
|
|
||||||
|
ARM_TEST_SRCS=$(wildcard $(ARM_SRC)/*.c)
|
||||||
|
ARM_TESTS+=$(patsubst $(ARM_SRC)/%.c, %, $(ARM_TEST_SRCS))
|
||||||
|
|
||||||
|
CRT_PATH=$(ARM_SRC)
|
||||||
|
LINK_SCRIPT=$(ARM_SRC)/kernel.ld
|
||||||
|
LDFLAGS=-Wl,-T$(LINK_SCRIPT)
|
||||||
|
CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC)
|
||||||
|
LDFLAGS+=-static -nostdlib -N $(CRT_OBJS) $(MINILIB_OBJS) -lgcc
|
||||||
|
|
||||||
|
# building head blobs
|
||||||
|
.PRECIOUS: $(CRT_OBJS)
|
||||||
|
|
||||||
|
%.o: $(ARM_SRC)/%.S
|
||||||
|
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@
|
||||||
|
|
||||||
|
# Build and link the tests
|
||||||
|
%: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
memory: CFLAGS+=-DCHECK_UNALIGNED=0
|
||||||
|
|
||||||
|
# Running
|
||||||
|
QEMU_BASE_MACHINE=-M virt -cpu max -display none
|
||||||
|
QEMU_OPTS+=$(QEMU_BASE_MACHINE) -semihosting-config enable=on,target=native,chardev=output -kernel
|
||||||
|
|
||||||
|
# Simple Record/Replay Test
|
||||||
|
.PHONY: memory-record
|
||||||
|
run-memory-record: memory-record memory
|
||||||
|
$(call run-test, $<, \
|
||||||
|
$(QEMU) -monitor none -display none \
|
||||||
|
-chardev file$(COMMA)path=$<.out$(COMMA)id=output \
|
||||||
|
-icount shift=5$(COMMA)rr=record$(COMMA)rrfile=record.bin \
|
||||||
|
$(QEMU_OPTS) memory)
|
||||||
|
|
||||||
|
.PHONY: memory-replay
|
||||||
|
run-memory-replay: memory-replay run-memory-record
|
||||||
|
$(call run-test, $<, \
|
||||||
|
$(QEMU) -monitor none -display none \
|
||||||
|
-chardev file$(COMMA)path=$<.out$(COMMA)id=output \
|
||||||
|
-icount shift=5$(COMMA)rr=replay$(COMMA)rrfile=record.bin \
|
||||||
|
$(QEMU_OPTS) memory)
|
||||||
|
|
||||||
|
EXTRA_RUNS+=run-memory-replay
|
||||||
|
|
||||||
|
TESTS += $(ARM_TESTS) $(MULTIARCH_TESTS)
|
||||||
|
EXTRA_RUNS+=$(MULTIARCH_RUNS)
|
||||||
|
|
|
@ -0,0 +1,319 @@
|
||||||
|
/*
|
||||||
|
* Minimal ArmV7 system boot code.
|
||||||
|
*
|
||||||
|
* Using semihosting for serial output and exit functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Semihosting interface on ARM AArch32
|
||||||
|
* R0 - semihosting call number
|
||||||
|
* R1 - semihosting parameter
|
||||||
|
*/
|
||||||
|
#define semihosting_call svc 0x123456
|
||||||
|
#define SYS_WRITEC 0x03 /* character to debug channel */
|
||||||
|
#define SYS_WRITE0 0x04 /* string to debug channel */
|
||||||
|
#define SYS_EXIT 0x18
|
||||||
|
|
||||||
|
#define ADP_Stopped_ApplicationExit 0x20026
|
||||||
|
#define ADP_Stopped_InternalError 0x20024
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper macro for annotating functions with elf type and size.
|
||||||
|
*/
|
||||||
|
.macro endf name
|
||||||
|
.global \name
|
||||||
|
.type \name, %function
|
||||||
|
.size \name, . - \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.section .interrupt_vector, "ax"
|
||||||
|
.align 5
|
||||||
|
|
||||||
|
vector_table:
|
||||||
|
b reset /* reset vector */
|
||||||
|
b undef_instr /* undefined instruction vector */
|
||||||
|
b software_intr /* software interrupt vector */
|
||||||
|
b prefetch_abort /* prefetch abort vector */
|
||||||
|
b data_abort /* data abort vector */
|
||||||
|
nop /* reserved */
|
||||||
|
b IRQ_handler /* IRQ vector */
|
||||||
|
b FIQ_handler /* FIQ vector */
|
||||||
|
|
||||||
|
endf vector_table
|
||||||
|
|
||||||
|
.text
|
||||||
|
__start:
|
||||||
|
ldr r0, =vector_table
|
||||||
|
mcr p15, 0, r0, c12, c0, 0 /* Set up VBAR */
|
||||||
|
|
||||||
|
ldr sp, =stack_end /* Set up the stack */
|
||||||
|
bl mmu_setup /* Set up the MMU */
|
||||||
|
bl main /* Jump to main */
|
||||||
|
|
||||||
|
endf __start
|
||||||
|
|
||||||
|
_exit:
|
||||||
|
cmp r0, #0
|
||||||
|
ite EQ // if-then-else. "EQ" is for if equal, else otherwise
|
||||||
|
ldreq r1, =ADP_Stopped_ApplicationExit // if r0 == 0
|
||||||
|
ldrne r1, =ADP_Stopped_InternalError // else
|
||||||
|
mov r0, #SYS_EXIT
|
||||||
|
semihosting_call
|
||||||
|
|
||||||
|
endf _exit
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
mmu_setup:
|
||||||
|
/*
|
||||||
|
* The MMU setup for this is very simple using two stage one
|
||||||
|
* translations. The first 1Mb section points to the text
|
||||||
|
* section and the second points to the data and rss.
|
||||||
|
* Currently the fattest test only needs ~50k for that so we
|
||||||
|
* have plenty of space.
|
||||||
|
*
|
||||||
|
* The short descriptor Section format is as follows:
|
||||||
|
*
|
||||||
|
* PA[31:20] - Section Base Address
|
||||||
|
* NS[19] - Non-secure bit
|
||||||
|
* 0[18] - Section (1 for Super Section)
|
||||||
|
* nG[17] - Not global bit
|
||||||
|
* S[16] - Shareable
|
||||||
|
* TEX[14:12] - Memory Region Attributes
|
||||||
|
* AP[15, 11:10] - Access Permission Bits
|
||||||
|
* IMPDEF[9]
|
||||||
|
* Domain[8:5]
|
||||||
|
* XN[4] - Execute never bit
|
||||||
|
* C[3] - Memory Region Attributes
|
||||||
|
* B[2] - Memory Region Attributes
|
||||||
|
* 1[1]
|
||||||
|
* PXN[0] - Privileged Execute Never
|
||||||
|
*
|
||||||
|
* r0 - point at the table
|
||||||
|
* r1 - address
|
||||||
|
* r2 - entry
|
||||||
|
* r3 - common section bits
|
||||||
|
* r4 - scratch
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory Region Bits
|
||||||
|
*
|
||||||
|
* TEX[14:12] = 000
|
||||||
|
* C[3] = 1
|
||||||
|
* B[2] = 1
|
||||||
|
*
|
||||||
|
* Outer and Inner WB, no write allocate
|
||||||
|
*/
|
||||||
|
mov r3, #0
|
||||||
|
ldr r4, =(3 << 2)
|
||||||
|
orr r3, r4, r4
|
||||||
|
|
||||||
|
/* Section bit */
|
||||||
|
orr r3, r3, #2
|
||||||
|
|
||||||
|
/* Page table setup (identity mapping). */
|
||||||
|
ldr r0, =ttb
|
||||||
|
|
||||||
|
/* First block: .text/RO/execute enabled */
|
||||||
|
ldr r1, =.text
|
||||||
|
ldr r2, =0xFFF00000 /* 1MB block alignment */
|
||||||
|
and r2, r1, r2
|
||||||
|
orr r2, r2, r3 /* common bits */
|
||||||
|
orr r2, r2, #(1 << 15) /* AP[2] = 1 */
|
||||||
|
orr r2, r2, #(1 << 10) /* AP[0] = 1 => RO @ PL1 */
|
||||||
|
|
||||||
|
lsr r4, r2, #(20 - 2)
|
||||||
|
str r2, [r0, r4, lsl #0] /* write entry */
|
||||||
|
|
||||||
|
/* Second block: .data/RW/no execute */
|
||||||
|
ldr r1, =.data
|
||||||
|
ldr r2, =0xFFF00000 /* 1MB block alignment */
|
||||||
|
and r2, r1, r2
|
||||||
|
orr r2, r2, r3 /* common bits */
|
||||||
|
orr r2, r2, #(1 << 10) /* AP[0] = 1 => RW @ PL1 */
|
||||||
|
orr r2, r2, #(1 << 4) /* XN[4] => no execute */
|
||||||
|
|
||||||
|
lsr r4, r2, #(20 - 2)
|
||||||
|
str r2, [r0, r4, lsl #0] /* write entry */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DACR - Domain Control
|
||||||
|
*
|
||||||
|
* Enable client mode for domain 0 (we don't use any others)
|
||||||
|
*/
|
||||||
|
ldr r0, =0x1
|
||||||
|
mcr p15, 0, r0, c3, c0, 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TTCBR - Translation Table Base Control Register
|
||||||
|
*
|
||||||
|
* EAE[31] = 0, 32-bit translation, short descriptor format
|
||||||
|
* N[2:0] = 5 ( TTBRO uses 31:14-5 => 9 bit lookup stage )
|
||||||
|
*/
|
||||||
|
ldr r0, =0x5
|
||||||
|
mcr p15, 0, r0, c1, c0, 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TTBR0 -Translation Table Base Register 0
|
||||||
|
*
|
||||||
|
* [31:9] = Base address of table
|
||||||
|
*
|
||||||
|
* QEMU doesn't really care about the cache sharing
|
||||||
|
* attributes so we don't need to either.
|
||||||
|
*/
|
||||||
|
ldr r0, =ttb
|
||||||
|
mcr p15, 0, r0, c2, c0, 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SCTLR- System Control Register
|
||||||
|
*
|
||||||
|
* TE[30] = 0, exceptions to A32 state
|
||||||
|
* AFE[29] = 0, AP[0] is the access permissions bit
|
||||||
|
* EE[25] = 0, Little-endian
|
||||||
|
* WXN[19] = 0 = no effect, Write does not imply XN (execute never)
|
||||||
|
* I[12] = Instruction cachability control
|
||||||
|
* C[2] = Data cachability control
|
||||||
|
* M[0] = 1, enable stage 1 address translation for EL0/1
|
||||||
|
*
|
||||||
|
* At this point virtual memory is enabled.
|
||||||
|
*/
|
||||||
|
ldr r0, =0x1005
|
||||||
|
mcr p15, 0, r0, c1, c0, 0
|
||||||
|
|
||||||
|
isb
|
||||||
|
|
||||||
|
mov pc, lr /* done, return to caller */
|
||||||
|
|
||||||
|
endf mmu_setup
|
||||||
|
|
||||||
|
/* Output a single character to serial port */
|
||||||
|
__sys_outc:
|
||||||
|
STMFD sp!, {r0-r1} // push r0, r1 onto stack
|
||||||
|
mov r1, sp
|
||||||
|
mov r0, #SYS_WRITEC
|
||||||
|
semihosting_call
|
||||||
|
LDMFD sp!, {r0-r1} // pop r0, r1 from stack
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
endf __sys_outc
|
||||||
|
|
||||||
|
reset:
|
||||||
|
ldr r1, =reset_error
|
||||||
|
b exception_handler
|
||||||
|
|
||||||
|
endf reset
|
||||||
|
|
||||||
|
undef_instr:
|
||||||
|
ldr r1, =undef_intr_error
|
||||||
|
b exception_handler
|
||||||
|
|
||||||
|
endf undef_instr
|
||||||
|
|
||||||
|
software_intr:
|
||||||
|
ldr r1, =software_intr_error
|
||||||
|
b exception_handler
|
||||||
|
|
||||||
|
endf software_intr
|
||||||
|
|
||||||
|
prefetch_abort:
|
||||||
|
ldr r1, =prefetch_abort_error
|
||||||
|
b exception_handler
|
||||||
|
|
||||||
|
endf prefetch_abort
|
||||||
|
|
||||||
|
data_abort:
|
||||||
|
ldr r1, =data_abort_error
|
||||||
|
b exception_handler
|
||||||
|
|
||||||
|
endf data_abort
|
||||||
|
|
||||||
|
IRQ_handler:
|
||||||
|
ldr r1, =irq_error
|
||||||
|
b exception_handler
|
||||||
|
|
||||||
|
endf IRQ_handler
|
||||||
|
|
||||||
|
FIQ_handler:
|
||||||
|
ldr r1, =fiq_error
|
||||||
|
b exception_handler
|
||||||
|
|
||||||
|
endf FIQ_handler
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initiate a exit semihosting call whenever there is any exception
|
||||||
|
* r1 already holds the string.
|
||||||
|
*/
|
||||||
|
exception_handler:
|
||||||
|
mov r0, #SYS_WRITE0
|
||||||
|
semihosting_call
|
||||||
|
mov r0, #SYS_EXIT
|
||||||
|
mov r1, #1
|
||||||
|
semihosting_call
|
||||||
|
|
||||||
|
endf exception_handler
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We implement a stub raise() function which errors out as tests
|
||||||
|
* shouldn't trigger maths errors.
|
||||||
|
*/
|
||||||
|
.global raise
|
||||||
|
raise:
|
||||||
|
mov r0, #SYS_WRITE0
|
||||||
|
ldr r1, =maths_error
|
||||||
|
semihosting_call
|
||||||
|
mov r0, #SYS_EXIT
|
||||||
|
ldr r1, =ADP_Stopped_InternalError
|
||||||
|
semihosting_call
|
||||||
|
|
||||||
|
endf raise
|
||||||
|
|
||||||
|
.data
|
||||||
|
|
||||||
|
.data
|
||||||
|
|
||||||
|
reset_error:
|
||||||
|
.ascii "Reset exception occurred.\n\0"
|
||||||
|
|
||||||
|
undef_intr_error:
|
||||||
|
.ascii "Undefined Instruction Exception Occurred.\n\0"
|
||||||
|
|
||||||
|
software_intr_error:
|
||||||
|
.ascii "Software Interrupt Occurred.\n\0"
|
||||||
|
|
||||||
|
prefetch_abort_error:
|
||||||
|
.ascii "Prefetch Abort Occurred.\n\0"
|
||||||
|
|
||||||
|
data_abort_error:
|
||||||
|
.ascii "Data Abort Occurred.\n\0"
|
||||||
|
|
||||||
|
irq_error:
|
||||||
|
.ascii "IRQ exception occurred.\n\0"
|
||||||
|
|
||||||
|
fiq_error:
|
||||||
|
.ascii "FIQ exception occurred.\n\0"
|
||||||
|
|
||||||
|
maths_error:
|
||||||
|
.ascii "Software maths exception.\n\0"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1st Stage Translation table
|
||||||
|
* 4096 entries, indexed by [31:20]
|
||||||
|
* each entry covers 1Mb of address space
|
||||||
|
* aligned on 16kb
|
||||||
|
*/
|
||||||
|
.align 15
|
||||||
|
ttb:
|
||||||
|
.space (4096 * 4), 0
|
||||||
|
|
||||||
|
.align 12
|
||||||
|
|
||||||
|
/* Space for stack */
|
||||||
|
.align 5
|
||||||
|
.section .bss
|
||||||
|
stack:
|
||||||
|
.space 65536, 0
|
||||||
|
stack_end:
|
|
@ -0,0 +1,24 @@
|
||||||
|
ENTRY(__start)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* virt machine, RAM starts at 1gb */
|
||||||
|
. = (1 << 30);
|
||||||
|
.text : {
|
||||||
|
*(.text)
|
||||||
|
}
|
||||||
|
.rodata : {
|
||||||
|
*(.rodata)
|
||||||
|
}
|
||||||
|
/* align r/w section to next 2mb */
|
||||||
|
. = ALIGN(1 << 21);
|
||||||
|
.data : {
|
||||||
|
*(.data)
|
||||||
|
}
|
||||||
|
.bss : {
|
||||||
|
*(.bss)
|
||||||
|
}
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.ARM.attributes)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue