/* * 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: