From f03fb5bad4428b30cdeab09c8e0b0f6074e0d882 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Sat, 9 May 2020 20:12:09 +0930 Subject: [PATCH] Add Linux loader This privdes just enough firmware to load Linux. The loader is linked to run from the aliased BRAM address. It can load a raw kernel (vmlinux.bin) or a wrapper that embeds the device tree (dtbImage.microwatt). The printf comes from https://github.com/mpredfearn/simple-printf 1. Build Linux for microwatt objcopy -O binary microwatt/vmlinux vmlinux.bin 2. Build a dtb 3. Set MW_DEBUG to point to a copy of mw_debug and type `make load` to load the loader into BRAM. 4. Press 'RESET' 5. Follow the instructions: Microwatt Loader (Jun 2 2020 21:31:16) Load binaries into SDRAM and select option to start: vmlinux.bin and dtb: mw_debug -b jtag stop load vmlinux.bin load microwatt.dtb 1000000 start press 'l' to start' dtbImage.microwatt: mw_debug -b jtag stop load dtbImage.microwatt 500000 start press 'w' to start' If you want to test other software, such as MicroPython, you can also load it to the start of SDRAM and the loader will jump to it with 'l'. Signed-off-by: Joel Stanley --- .gitignore | 9 + loader/Makefile | 41 +++++ loader/head.S | 109 ++++++++++++ loader/load.S | 26 +++ loader/loader.c | 63 +++++++ loader/powerpc.lds.S | 17 ++ loader/printf.c | 413 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 678 insertions(+) create mode 100644 loader/Makefile create mode 100644 loader/head.S create mode 100644 loader/load.S create mode 100644 loader/loader.c create mode 100644 loader/powerpc.lds.S create mode 100644 loader/printf.c diff --git a/.gitignore b/.gitignore index ee0854943..19fb9af8d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,17 @@ *.cf *.s *_tb +*.swp main_ram.bin tests/*/*.bin tests/*/*.hex tests/*/*.elf TAGS +tags +scripts/mw_debug/mw_debug +loader/loader.bin +loader/loader.hex +loader/loader.elf +loader/powerpc.lds +*.swn +*.swo diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 000000000..f67fba39b --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,41 @@ +ARCH = $(shell uname -m) +ifneq ("$(ARCH)", "ppc64") +ifneq ("$(ARCH)", "ppc64le") + CROSS_COMPILE ?= powerpc64le-linux-gnu- +endif +endif + +PYTHON3 ?= python3 +MW_DEBUG ?= mw_debug +BRAM_ADDRESS ?= 0x80000000 + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS = -Os -g -Wall -std=c99 -msoft-float -mno-string -mno-multiple \ + -mno-vsx -mno-altivec -mlittle-endian -fno-stack-protector \ + -mstrict-align -ffreestanding -fdata-sections -ffunction-sections \ + -I../include +ASFLAGS = $(CFLAGS) +LDFLAGS = -T powerpc.lds --gc-sections + +all: loader.hex + +load: loader.bin + $(MW_DEBUG) -b jtag load $^ $(BRAM_ADDRESS) + +%.lds : %.lds.S + $(CC) -I../include -P -E $< -o $@ + +loader.elf: loader.o head.o ../lib/console.o load.o printf.o | powerpc.lds + $(LD) $(LDFLAGS) -o $@ $^ + +loader.bin: loader.elf + $(OBJCOPY) -O binary $^ $@ + +loader.hex: loader.bin + $(PYTHON3) ../scripts/bin2hex.py $^ > $@ + +clean: + @rm -f *.o loader.elf loader.bin loader.hex powerpc.lds diff --git a/loader/head.S b/loader/head.S new file mode 100644 index 000000000..82c81a559 --- /dev/null +++ b/loader/head.S @@ -0,0 +1,109 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define STACK_TOP (BRAM_BASE + 0x2000) + +#define FIXUP_ENDIAN \ + tdi 0,0,0x48; /* Reverse endian of b . + 8 */ \ + b 191f; /* Skip trampoline if endian is good */ \ + .long 0xa600607d; /* mfmsr r11 */ \ + .long 0x01006b69; /* xori r11,r11,1 */ \ + .long 0x05009f42; /* bcl 20,31,$+4 */ \ + .long 0xa602487d; /* mflr r10 */ \ + .long 0x14004a39; /* addi r10,r10,20 */ \ + .long 0xa64b5a7d; /* mthsrr0 r10 */ \ + .long 0xa64b7b7d; /* mthsrr1 r11 */ \ + .long 0x2402004c; /* hrfid */ \ +191: + + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + /* + * Microwatt currently enters in LE mode at 0x0, so we don't need to + * do any endian fix ups> + */ + . = 0 +.global _start +_start: + b boot_entry + + /* QEMU enters at 0x10 */ + . = 0x10 + FIXUP_ENDIAN + b boot_entry + + . = 0x100 + FIXUP_ENDIAN + b boot_entry + +.global boot_entry +boot_entry: + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12, + bctrl + b . + +#define EXCEPTION(nr) \ + .= nr ;\ + b . + + /* More exception stubs */ + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + EXCEPTION(0x500) + EXCEPTION(0x600) + EXCEPTION(0x700) + EXCEPTION(0x800) + EXCEPTION(0x900) + EXCEPTION(0x980) + EXCEPTION(0xa00) + EXCEPTION(0xb00) + EXCEPTION(0xc00) + EXCEPTION(0xd00) + EXCEPTION(0xe00) + EXCEPTION(0xe20) + EXCEPTION(0xe40) + EXCEPTION(0xe60) + EXCEPTION(0xe80) + EXCEPTION(0xf00) + EXCEPTION(0xf20) + EXCEPTION(0xf40) + EXCEPTION(0xf60) + EXCEPTION(0xf80) +#if 0 + EXCEPTION(0x1000) + EXCEPTION(0x1100) + EXCEPTION(0x1200) + EXCEPTION(0x1300) + EXCEPTION(0x1400) + EXCEPTION(0x1500) + EXCEPTION(0x1600) +#endif diff --git a/loader/load.S b/loader/load.S new file mode 100644 index 000000000..2bae99149 --- /dev/null +++ b/loader/load.S @@ -0,0 +1,26 @@ +/* + * Set up system for jumping to Linux. + * + * load_linux(kernel, dtb) + */ +.global load_linux +load_linux: + /* ABI: + * r5: zero (no openfirmware) + * r3: device tree + */ + addi %r5,0,0 + addi %r12,%r3,0 + addi %r3,%r4,0 + mtctr %r12 + bctrl + b . + +/* + * Invalidate the icache. + */ +.global invalidate_icache +invalidate_icache: + icbi 0,0 + isync + blr diff --git a/loader/loader.c b/loader/loader.c new file mode 100644 index 000000000..9b4096831 --- /dev/null +++ b/loader/loader.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include + +#include "console.h" + +extern void load_linux(unsigned long kernel, unsigned long dtb); +extern void invalidate_icache(void); + +extern int simple_printf(char *fmt, ...); +#define printf(...) simple_printf(__VA_ARGS__) + +#define DTB_ADDR 0x01000000L + +#define DTBIMAGE_ADDR 0x500000L + +int main(void) +{ + int i; + potato_uart_init(); + unsigned long kernel, dtb; + + printf("\r\nMicrowatt Loader (%s %s)\r\n\r\n", __DATE__, __TIME__); + + writeq(SYS_REG_CTRL_DRAM_AT_0, SYSCON_BASE + SYS_REG_CTRL); + invalidate_icache(); + + printf("Load binaries into SDRAM and select option to start:\r\n\r\n"); + printf("vmlinux.bin and dtb:\r\n"); + printf(" mw_debug -b jtag stop load vmlinux.bin load microwatt.dtb %x start\r\n", + DTB_ADDR); + printf(" press 'l' to start'\r\n\r\n"); + + printf("dtbImage.microwatt:\r\n"); + printf(" mw_debug -b jtag stop load dtbImage.microwatt %x start\r\n", + DTBIMAGE_ADDR); + printf(" press 'w' to start'\r\n\r\n"); + + while (1) { + switch (getchar()) { + case 'l': + kernel = 0; + dtb = DTB_ADDR; + goto load; + case 'w': + kernel = DTBIMAGE_ADDR; + dtb = -1; + goto load; + default: + continue; + } + } + +load: + printf("Loading Linux at %08x...\r\n", 0x0); + for (i = 0; i < 80; i++) + putchar('.'); + printf("\r\n"); + load_linux(kernel, dtb); + + while (1); +} diff --git a/loader/powerpc.lds.S b/loader/powerpc.lds.S new file mode 100644 index 000000000..dbda5651e --- /dev/null +++ b/loader/powerpc.lds.S @@ -0,0 +1,17 @@ +#include + +#define BASE_ADDR BRAM_BASE + +SECTIONS +{ + _start = .; + . = BASE_ADDR; + .head : { + KEEP(*(.head)) + } + . = (BASE_ADDR + 0x1000); + .text : { *(.text) } + . = (BASE_ADDR + 0x2000); + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/loader/printf.c b/loader/printf.c new file mode 100644 index 000000000..9e8bd57cb --- /dev/null +++ b/loader/printf.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2016, Matt Redfearn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 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. + */ + +#include +#include + +int putchar(int c); + +static void simple_outputchar(char **str, char c) +{ + if (str) { + **str = c; + ++(*str); + } else { + putchar(c); + } +} + +enum flags { + PAD_ZERO = 1, + PAD_RIGHT = 2, +}; + +static int prints(char **out, const char *string, int width, int flags) +{ + int pc = 0, padchar = ' '; + + if (width > 0) { + int len = 0; + const char *ptr; + for (ptr = string; *ptr; ++ptr) ++len; + if (len >= width) width = 0; + else width -= len; + if (flags & PAD_ZERO) + padchar = '0'; + } + if (!(flags & PAD_RIGHT)) { + for ( ; width > 0; --width) { + simple_outputchar(out, padchar); + ++pc; + } + } + for ( ; *string ; ++string) { + simple_outputchar(out, *string); + ++pc; + } + for ( ; width > 0; --width) { + simple_outputchar(out, padchar); + ++pc; + } + + return pc; +} + +#define PRINT_BUF_LEN 64 + +static int simple_outputi(char **out, long long i, int base, int sign, int width, int flags, int letbase) +{ + char print_buf[PRINT_BUF_LEN]; + char *s; + int t, neg = 0, pc = 0; + unsigned long long u = i; + + if (i == 0) { + print_buf[0] = '0'; + print_buf[1] = '\0'; + return prints(out, print_buf, width, flags); + } + + if (sign && base == 10 && i < 0) { + neg = 1; + u = -i; + } + + s = print_buf + PRINT_BUF_LEN-1; + *s = '\0'; + + while (u) { + t = u % base; + if( t >= 10 ) + t += letbase - '0' - 10; + *--s = t + '0'; + u /= base; + } + + if (neg) { + if( width && (flags & PAD_ZERO) ) { + simple_outputchar (out, '-'); + ++pc; + --width; + } + else { + *--s = '-'; + } + } + + return pc + prints (out, s, width, flags); +} + + +static int simple_vsprintf(char **out, char *format, va_list ap) +{ + int width, flags; + int pc = 0; + char scr[2]; + union { + char c; + char *s; + int i; + unsigned int u; + long li; + unsigned long lu; + long long lli; + unsigned long long llu; + short hi; + unsigned short hu; + signed char hhi; + unsigned char hhu; + void *p; + } u; + + for (; *format != 0; ++format) { + if (*format == '%') { + ++format; + width = flags = 0; + if (*format == '\0') + break; + if (*format == '%') + goto out; + if (*format == '-') { + ++format; + flags = PAD_RIGHT; + } + while (*format == '0') { + ++format; + flags |= PAD_ZERO; + } + if (*format == '*') { + width = va_arg(ap, int); + format++; + } else { + for ( ; *format >= '0' && *format <= '9'; ++format) { + width *= 10; + width += *format - '0'; + } + } + switch (*format) { + case('d'): + u.i = va_arg(ap, int); + pc += simple_outputi(out, u.i, 10, 1, width, flags, 'a'); + break; + + case('u'): + u.u = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.u, 10, 0, width, flags, 'a'); + break; + + case('x'): + u.u = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.u, 16, 0, width, flags, 'a'); + break; + + case('X'): + u.u = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.u, 16, 0, width, flags, 'A'); + break; + + case('c'): + u.c = va_arg(ap, int); + scr[0] = u.c; + scr[1] = '\0'; + pc += prints(out, scr, width, flags); + break; + + case('s'): + u.s = va_arg(ap, char *); + pc += prints(out, u.s ? u.s : "(null)", width, flags); + break; + case('l'): + ++format; + switch (*format) { + case('d'): + u.li = va_arg(ap, long); + pc += simple_outputi(out, u.li, 10, 1, width, flags, 'a'); + break; + + case('u'): + u.lu = va_arg(ap, unsigned long); + pc += simple_outputi(out, u.lu, 10, 0, width, flags, 'a'); + break; + + case('x'): + u.lu = va_arg(ap, unsigned long); + pc += simple_outputi(out, u.lu, 16, 0, width, flags, 'a'); + break; + + case('X'): + u.lu = va_arg(ap, unsigned long); + pc += simple_outputi(out, u.lu, 16, 0, width, flags, 'A'); + break; + + case('l'): + ++format; + switch (*format) { + case('d'): + u.lli = va_arg(ap, long long); + pc += simple_outputi(out, u.lli, 10, 1, width, flags, 'a'); + break; + + case('u'): + u.llu = va_arg(ap, unsigned long long); + pc += simple_outputi(out, u.llu, 10, 0, width, flags, 'a'); + break; + + case('x'): + u.llu = va_arg(ap, unsigned long long); + pc += simple_outputi(out, u.llu, 16, 0, width, flags, 'a'); + break; + + case('X'): + u.llu = va_arg(ap, unsigned long long); + pc += simple_outputi(out, u.llu, 16, 0, width, flags, 'A'); + break; + + default: + break; + } + break; + default: + break; + } + break; + case('h'): + ++format; + switch (*format) { + case('d'): + u.hi = va_arg(ap, int); + pc += simple_outputi(out, u.hi, 10, 1, width, flags, 'a'); + break; + + case('u'): + u.hu = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.lli, 10, 0, width, flags, 'a'); + break; + + case('x'): + u.hu = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.lli, 16, 0, width, flags, 'a'); + break; + + case('X'): + u.hu = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.lli, 16, 0, width, flags, 'A'); + break; + + case('h'): + ++format; + switch (*format) { + case('d'): + u.hhi = va_arg(ap, int); + pc += simple_outputi(out, u.hhi, 10, 1, width, flags, 'a'); + break; + + case('u'): + u.hhu = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.lli, 10, 0, width, flags, 'a'); + break; + + case('x'): + u.hhu = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.lli, 16, 0, width, flags, 'a'); + break; + + case('X'): + u.hhu = va_arg(ap, unsigned int); + pc += simple_outputi(out, u.lli, 16, 0, width, flags, 'A'); + break; + + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + } + else { +out: + simple_outputchar (out, *format); + ++pc; + } + } + if (out) **out = '\0'; + return pc; +} + +int simple_printf(char *fmt, ...) +{ + va_list ap; + int r; + + va_start(ap, fmt); + r = simple_vsprintf(NULL, fmt, ap); + va_end(ap); + + return r; +} + +int simple_sprintf(char *buf, char *fmt, ...) +{ + va_list ap; + int r; + + va_start(ap, fmt); + r = simple_vsprintf(&buf, fmt, ap); + va_end(ap); + + return r; +} + + +#ifdef TEST + +#define printf simple_printf +#define sprintf simple_sprintf + +int main(int argc, char *argv[]) +{ + static char shortstr[] = "Test"; + char buf[256]; + + printf("percent: \"%%\"\n"); + printf("bad format: \"%z\"\n"); + printf("\n"); + printf("decimal: \"%d\"\n", 12345); + printf("decimal negative: \"%d\"\n", -2345); + printf("\n"); + printf("unsigned: \"%u\"\n", 12345); + printf("unsigned negative: \"%u\"\n", -2345); + printf("\n"); + printf("hex: \"%x\"\n", 0x12345); + printf("hex negative: \"%x\"\n", -0x12345); + printf("\n"); + printf("long decimal: \"%ld\"\n", 123456L); + printf("long decimal negative: \"%ld\"\n", -23456L); + printf("\n"); + printf("long unsigned: \"%lu\"\n", 123456L); + printf("long unsigned negative: \"%lu\"\n", -123456L); + printf("\n"); + printf("long hex: \"%lx\"\n", 0x12345L); + printf("long hex negative: \"%lx\"\n", -0x12345L); + printf("\n"); + printf("long long decimal: \"%lld\"\n", 123456LL); + printf("long long decimal negative: \"%lld\"\n", -23456LL); + printf("\n"); + printf("long long unsigned: \"%llu\"\n", 123456LL); + printf("long long unsigned negative: \"%llu\"\n", -123456LL); + printf("\n"); + printf("long long hex: \"%llx\"\n", 0x12345LL); + printf("long long hex negative: \"%llx\"\n", -0x12345LL); + printf("\n"); + printf("zero-padded LD: \"%010ld\"\n", 123456); + printf("zero-padded LDN: \"%010ld\"\n", -123456); + printf("left-adjusted ZLDN: \"%-010ld\"\n", -123456); + printf("space-padded LDN: \"%10ld\"\n", -123456); + printf("left-adjusted SLDN: \"%-10ld\"\n", -123456); + printf("\n"); + printf("variable pad width: \"%0*d\"\n", 15, -2345); + printf("\n"); + printf("char: \"%c%c%c%c\"\n", 'T', 'e', 's', 't'); + printf("\n"); + printf("zero-padded string: \"%010s\"\n", shortstr); + printf("left-adjusted Z string: \"%-010s\"\n", shortstr); + printf("space-padded string: \"%10s\"\n", shortstr); + printf("left-adjusted S string: \"%-10s\"\n", shortstr); + printf("null string: \"%s\"\n", (char *)NULL); + + sprintf(buf, "decimal:\t\"%d\"\n", -2345); + printf("sprintf: %s", buf); +} +#endif /* TEST */