diff mbox

[2/2] boot-wrapper: Support reading kernel/initrd via semihosting

Message ID 1327432738-4026-3-git-send-email-peter.maydell@linaro.org
State Accepted
Headers show

Commit Message

Peter Maydell Jan. 24, 2012, 7:18 p.m. UTC
Implement support in the boot-wrapper for reading our kernel,
initrd and command line arguments using the semihosting ABI
provided by a Fast Model. We build a second AXF file
linux-system-semi.axf which does not depend on or bake in
a kernel binary. Instead when you start the model with it
you pass the model a parameter like:
  -C cluster.cpu0.semihosting-cmd_line="--kernel /path/to/uImage
  --initrd /path/to/initrd -- console=ttyAMA0 mem=512M ..."

The boot wrapper will interpret the --kernel and --initrd
arguments as taking an option which is a (host system)
path, and pass the tail of the string following '--' to
the kernel as its command line.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile    |   12 ++-
 boot.S      |  355 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 model.lds.S |   12 ++-
 3 files changed, 376 insertions(+), 3 deletions(-)
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 54cdbf5..ebcbce5 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@  BOOTLOADER	= boot.S
 KERNEL		= uImage
 
 IMAGE		= linux-system.axf
+SEMIIMG 	= linux-system-semi.axf
 LD_SCRIPT	= model.lds.S
 
 
@@ -27,7 +28,7 @@  LD		= $(CROSS_COMPILE)ld
 # These are needed by the underlying kernel make
 export CROSS_COMPILE ARCH
 
-all: $(IMAGE)
+all: $(IMAGE) $(SEMIIMG)
 
 clean:
 	rm -f $(IMAGE) boot.o model.lds monitor.o uImage
@@ -38,15 +39,24 @@  $(KERNEL): $(KERNEL_SRC)/arch/arm/boot/uImage
 $(IMAGE): boot.o monitor.o model.lds $(KERNEL) $(FILESYSTEM) Makefile
 	$(LD) -o $@ --script=model.lds
 
+$(SEMIIMG): bootsemi.o monitor.o modelsemi.lds
+	$(LD) -o $@ --script=modelsemi.lds
+
 boot.o: $(BOOTLOADER)
 	$(CC) $(CPPFLAGS) -DKCMD='$(KCMD)' -c -o $@ $<
 
+bootsemi.o: $(BOOTLOADER)
+	$(CC) $(CPPFLAGS) -DSEMIHOSTING=1 -c -o $@ $<
+
 monitor.o: $(MONITOR)
 	$(CC) $(CPPFLAGS) -c -o $@ $<
 
 model.lds: $(LD_SCRIPT) Makefile
 	$(CC) $(CPPFLAGS) -E -P -C -o $@ $<
 
+modelsemi.lds: $(LD_SCRIPT) Makefile
+	$(CC) $(CPPFLAGS) -DSEMIHOSTING=1 -E -P -C -o $@ $<
+
 $(KERNEL_SRC)/arch/arm/boot/uImage: force
 	$(MAKE) -C $(KERNEL_SRC) -j4 uImage
 
diff --git a/boot.S b/boot.S
index 4629734..cf8bdb0 100644
--- a/boot.S
+++ b/boot.S
@@ -11,6 +11,49 @@ 
 	.arch_extension sec
 	.text
 
+#ifdef SEMIHOSTING
+@ Helper definitions and macros for semihosting
+SYS_OPEN = 0x01
+SYS_CLOSE = 0x02
+SYS_WRITE0 = 0x04
+SYS_READ = 0x06
+SYS_FLEN = 0x0C
+SYS_GET_CMDLINE = 0x15
+SYS_REPORTEXC = 0x18
+
+.macro semihost what
+	@ Make a semihosting call. Note that on return r0 is always
+	@ either the return value or is trashed.
+	mov     r0, \what
+#if defined(MACH_MPS)
+	@ M profile semihosting is via bpkt
+	bkpt    0xab
+#elif defined(__thumb__)
+	@ Otherwise, different SVC numbers for ARM or Thumb mode
+	svc    0xab
+#else
+	svc     0x123456
+#endif
+.endm
+
+.macro exit
+	@ Exit via semihosting call
+	@ REASON_APP_EXIT = 0x20026
+	mov r1, 0x20000
+	orr r1, r1, 0x26
+	semihost SYS_REPORTEXC
+	@ This point is never reached.
+.endm
+
+.macro fail string
+	@ Print the message pointed to by string out via semihosting,
+	@ and then exit.
+	ldr r1, =\string
+	semihost SYS_WRITE0
+	exit
+.endm
+#endif
+
 	.globl	_start
 _start:
 #ifdef SMP
@@ -114,6 +157,225 @@  _start:
 	orr	r1, #0x0001			@ cr
 	str	r1, [r0, #0x30]
 
+#ifdef SEMIHOSTING
+	@
+	@ Get kernel, initrd, command line via semihosting
+	@
+
+	mov     r0, SYS_GET_CMDLINE
+	ldr     r1, =semiblk
+	ldr     r2, =sh_cmdline
+	str     r2, [r1]
+	@ We have huge amounts of space between us and where we're
+	@ going to put the kernel, so we can allow for an enormous
+	@ command line length.
+	mov     r3, 0x2000
+	str     r3, [r1, #4]
+	semihost SYS_GET_CMDLINE
+	cmp     r0, 0
+	bne     1f
+	ldr     r3, [r1, #4]
+	cmp     r3, 0x2000
+	blt     2f
+1:
+	@ Failed to get command line, or truncated
+	fail get_cmd_failed
+
+2:
+	@ That gives us one big NUL terminated string.
+	@ We expect:
+	@ --kernel kernelfilename
+	@ --initrd initrdfilename
+	@ --
+	@ and the remainder of the string is kernel args
+
+	@ r2 is still the command line here
+1:
+	@ Skip any leading whitespace, stop if we hit the end
+	ldrb r0, [r2]
+	cmp r0, 32
+	bne 2f
+	add r2, r2, 1
+	b 1b
+2:
+	cmp r0, 0
+	bne 3f
+	@ If we hit the end of the string before '--' it's invalid
+	fail bad_cmd_line
+3:
+	ldr r1, =dashdashkernel
+	bl matchword
+	cmp r0, 0
+	beq 2f
+	@ found --kernel, r2 points at next word
+	@ load at offset -0x40 to allow for uImage header
+	ldr r1, =kernel
+	sub r1, r1, 0x40
+	bl loadfile
+	b 1b
+
+2:
+	adr r1, dashdashinitrd
+	bl matchword
+	cmp r0, 0
+	beq 2f
+	@ found --initrd, r2 points at next word
+	ldr r1, =filesystem
+	bl loadfile
+	@ now r3 is length of the initrd, fix up our ATAGS
+	ldr r1, =atag_initrd_sz
+	str r3, [r1]
+	b 1b
+
+2:
+	adr r1, dashdash
+	bl matchword
+	cmp r0, 0
+	beq 2f
+	@ found --, r2 points at next word
+	@ handle rest of string (if any) as kernel args
+	@ If we didn't have an initrd, write the ATAG_CMDLINE
+	@ over the top of the ATAG_INITRD2
+	ldr r0, =atag_cmdline
+	ldr r4, [r0, #4]   @ ATAG_CMDLINE magic number
+	ldr r1, =atag_initrd_sz
+	ldr r1, [r1]
+	cmp r1, 0
+        itt eq
+	ldreq r0, =atag_initrd
+	streq r4, [r0, #4]
+	add r3, r0, #8
+	@ copy string from r2 to r3
+1:      ldrb r1, [r2],#1
+	strb r1, [r3],#1
+	cmp r1, 0
+	bne 1b
+	@ zero-pad to word boundary
+1:      tst r3, #3
+	beq 1f
+	strb r1, [r3],#1
+	b 1b
+1:      @ now write the length word
+	sub r4, r3, r0
+	lsr r4, r4, #2
+	str r4, [r0]
+	@ and terminate the ATAGS list with an ATAG_NONE node
+	str r1, [r3, #0]
+	str r1, [r3, #4]
+
+	b run_kernel
+
+2:
+	@ unrecognised option
+	fail bad_cmd_line
+
+matchword:
+	@ Subroutine: if the first word (up to space or end)
+	@ in the string r2 matches the word pointed at by r1
+	@ then return with r0 != 0 and r2 pointing at the
+	@ space/end. Otherwise return with r0 = 0, r2 unchanged.
+	mov r3, r2
+1:
+	ldrb r0, [r2]
+	ldrb r4, [r1]
+	cmp r4, 0
+	beq 1f
+	cmp r4, r0
+	bne matchfail
+	add r2, r2, 1
+	add r1, r1, 1
+	b 1b
+1:
+	@ end of matched string, is r2 at end of word?
+	cmp r0, 32
+        itt ne
+	cmpne r0, 0
+	bne matchfail
+	@ Success
+	mov r0, 1
+	bx lr
+1:      @ not end of string, match?
+matchfail:
+	mov r2, r3
+	mov r0, 0
+	bx lr
+
+loadfile:
+	@ Subroutine: r2 points to a filename argument (possibly with
+	@ leading spaces, space or NUL terminated), r1 is the address
+	@ to load it at. Load the file via semihosting to the
+	@ specified address. On exit r2 points to the space/NUL
+	@ after the filename, and r3 is the length of the file in bytes
+	@ We modify the filename string in place, temporarily
+	mov r5, r1
+	adr r1, loading_str
+	semihost SYS_WRITE0
+	@ skip leading spaces
+1:      ldrb r0, [r2]
+	cmp r0, 32
+	bne 1f
+	add r2, r2, 1
+	b 1b
+1:      mov r3, r2
+	@ advance until next space or NUL
+1:      ldrb r0, [r2]
+	cmp r0, 32
+        ite ne
+	cmpne r0, 0
+	beq 1f
+	add r2, r2, 1
+	b 1b
+1:      @ filename is r3 to r2, save terminating byte and nul terminate
+	mov r4, r0
+	mov r0, 0
+	strb r0, [r2]
+	mov r1, r3
+	semihost SYS_WRITE0
+	adr r1, colonspace_str
+	semihost SYS_WRITE0
+	adr r1, semiblk
+	str r3, [r1]     @ filename
+	mov r0, 1
+	str r0, [r1, #4] @ file mode: "rb"
+	subs r0, r2, r3
+	bne 1f
+	fail nofilename
+1:      str r0, [r1, #8] @ file name length
+	semihost SYS_OPEN
+	cmp r0, -1
+	bne 1f
+	fail openfailed
+1:      @ now we can restore the terminating byte
+	strb r4, [r2]
+	mov r4, r0
+	str r0, [r1]     @ filehandle
+	semihost SYS_FLEN
+	cmp r0, -1
+	bne 1f
+	fail flenfailed
+1:      adr r1, semiblk
+	mov r3, r0
+	str r4, [r1]     @ filehandle
+	str r5, [r1, #4] @ buffer
+	str r0, [r1, #8] @ length
+	semihost SYS_READ
+	cmp r0, 0
+	beq 1f
+	fail readfailed
+1:      adr r1, semiblk
+	str r4, [r1]
+	semihost SYS_CLOSE
+	cmp r0, 0
+	beq 1f
+	fail closefailed
+1:      @ Success! r2 is pointing to the space/NUL after the filename,
+	@ r3 is the length of the file in bytes
+	adr r1, ok_str
+	semihost SYS_WRITE0
+	bx lr
+
+#endif
+run_kernel:
 	@
 	@ Kernel parameters
 	@
@@ -133,7 +395,98 @@  _start:
 #endif
 	mov     pc, lr				@ jump to the kernel
 
+
+	@
+	@ Data
+	@
+
+#ifdef SEMIHOSTING
+	.org 0x500
+	@ Put the constant pool here as otherwise the assembler will
+	@ put it immediately after our atags and it will be overwritten
+	@ by the semihosting command line.
+	.ltorg
+
+	@ block of four words used to pass/return values in semihosting calls
+	.align 2
+semiblk:
+	.long 0,0,0,0
+
+dashdashkernel:
+	.asciz "--kernel"
+	.align 2
+dashdashinitrd:
+	.asciz "--initrd"
+	.align 2
+dashdash:
+	.asciz "--"
+	.align 2
+
+get_cmd_failed:
+	.asciz "Failed to get semihosting command line\n"
+	.align 2
+bad_cmd_line:
+	.asciz "Bad command line format (unknown option?)\n"
+	.align 2
+nofilename:
+	.asciz "Expected filename argument\n"
+	.align 2
+openfailed:
+	.asciz "open failed!\n"
+	.align 2
+flenfailed:
+	.asciz "could not find length of file!\n"
+	.align 2
+readfailed:
+	.asciz "could not read file!\n"
+	.align 2
+closefailed:
+	.asciz "could not close file!\n"
+	.align 2
+loading_str:
+	.asciz "Loading: "
+	.align 2
+colonspace_str:
+	.asciz ": "
+	.align 2
+ok_str:
+	.asciz "OK\n"
+	.align 2
+atags:
+	@ Template for our ATAGs: we will edit these: the INITRD2
+	@ is optional and the CMDLINE will be expanded to include
+	@ the actual command line.
+	@ The important thing here is that any editing we do
+	@ to the template should only make it shorter, so we
+	@ don't accidentally overwrite the semihosting commandline
+	@ until the very end when we copy the tail end of the
+	@ semihosting command line into the ATAG_CMDLINE node
+	@ as the kernel parameters.
+
+	@ ATAG_CORE
+	.long 2
+	.long 0x54410001
+
+	@ ATAG_INITRD2
+atag_initrd:
+	.long	4
+	.long	0x54420005
+	.long	filesystem    @ address
+atag_initrd_sz:
+	.long	0        @ size
+
+	@ ATAG_CMDLINE
+atag_cmdline:
+	.long 0          @ length
+	.long 0x54410009
+	@ command line string will start here
+
+sh_cmdline:
+	@ Semihosting command line will be written here
+
+#else /* not SEMIHOSTING */
 	.org	0x100
+	@ Static ATAGS for when kernel/etc are compiled into the ELF file
 atags:
 	@ ATAG_CORE
 	.long	2
@@ -158,3 +511,5 @@  atags:
 	@ ATAG_NONE
 	.long	0
 	.long	0x00000000
+
+#endif /* not SEMIHOSTING */
diff --git a/model.lds.S b/model.lds.S
index 4769480..3af4e07 100644
--- a/model.lds.S
+++ b/model.lds.S
@@ -13,11 +13,12 @@  TARGET(binary)
 
 INPUT(./monitor.o)
 INPUT(./boot.o)
+#ifndef SEMIHOSTING
 INPUT(./uImage)
 #ifdef USE_INITRD
  INPUT(./filesystem.cpio.gz)
 #endif
-
+#endif
 
 
 
@@ -30,15 +31,22 @@  MON_OFFSET  = 0xf0000000;
 SECTIONS
 {
  . = PHYS_OFFSET;
+
+#ifdef SEMIHOSTING
+ .text : { bootsemi.o }
+#else
  .text : { boot.o }
+#endif
 
  . = PHYS_OFFSET + 0x8000 - 0x40;
  kernel = . + 0x40;
+#ifndef SEMIHOSTING
  .kernel : { ./uImage }
+#endif
 
  . = PHYS_OFFSET + 0x00800000;
  filesystem = .;
-#ifdef USE_INITRD
+#if defined(USE_INITRD) && !defined(SEMIHOSTING)
  .filesystem : { ./filesystem.cpio.gz }
  fs_size = . - filesystem;
 #endif