 56611e17d2
			
		
	
	
		56611e17d2
		
	
	
	
	
		
			
			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>
		
			
				
	
	
		
			320 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| /*
 | |
|  * 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:
 |