diff --git a/.gitignore b/.gitignore index 3829b90..9afeb0a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.cf *.s *_tb +*.swp main_ram.bin tests/*/*.bin tests/*/*.hex @@ -10,3 +11,11 @@ tests/*/*.elf TAGS litedram/build/* obj_dir/* +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 0000000..f434e0e --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,64 @@ +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 + +# Use make V=1 for a verbose build. +ifndef V + Q_CC= @echo ' [CC] ' $@; + Q_LINK= @echo ' [LINK] ' $@; + Q_OBJCOPY=@echo ' [OBJCOPY] ' $@; + Q_PYTHON= @echo ' [PYTHON] ' $@; +endif + +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 -nostdinc -flto \ + -Ilibc/include/ -I../include -isystem $(shell $(CC) -print-file-name=include) \ + -D__USE_LIBC +ASFLAGS = $(CFLAGS) +LDFLAGS = -T powerpc.lds -static -nostdlib -Wl,--gc-sections -Wl,--build-id=none + +LIBC_SRC := libc/src/isdigit.c libc/src/memcmp.c libc/src/strcat.c \ + libc/src/strncasecmp.c libc/src/strtok.c libc/src/vsnprintf.c \ + libc/src/isprint.c libc/src/memcpy.c libc/src/strchr.c libc/src/strncmp.c \ + libc/src/strtol.c libc/src/isspace.c libc/src/memmove.c libc/src/strcmp.c \ + libc/src/strncpy.c libc/src/strtoul.c libc/src/isxdigit.c libc/src/memset.c \ + libc/src/strcpy.c libc/src/strrchr.c libc/src/tolower.c libc/src/memchr.c \ + libc/src/strcasecmp.c libc/src/strlen.c libc/src/strstr.c libc/src/toupper.c +LIBC_OBJ := $(LIBC_SRC:.c=.o) + +COMPILE.c = $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c +COMPILE.S = $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c + +all: loader.hex + +load: loader.bin + $(MW_DEBUG) -b jtag load $^ $(BRAM_ADDRESS) + +%.lds : %.lds.S + $(Q_CC)$(CC) -I../include -P -E $< -o $@ + +loader.elf: loader.o head.o ../lib/console.o $(LIBC_OBJ) | powerpc.lds + $(Q_LINK)$(CC) $(LDFLAGS) -o $@ $^ + @size $@ + +loader.bin: loader.elf + $(Q_OBJCOPY)$(OBJCOPY) -O binary $^ $@ + +loader.hex: loader.bin + $(Q_PYTHON)$(PYTHON3) ../scripts/bin2hex.py $^ > $@ + +.PHONY: +clean: + @rm -f *.o $(LIBC_OBJ) ../lib/console.o loader.elf loader.bin loader.hex powerpc.lds diff --git a/loader/head.S b/loader/head.S new file mode 100644 index 0000000..a141c20 --- /dev/null +++ b/loader/head.S @@ -0,0 +1,38 @@ +/* Copyright 2020 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 + 0x3000) + +/* 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" + +.global start +start: + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12, + bctrl + b . diff --git a/loader/loader.c b/loader/loader.c new file mode 100644 index 0000000..d8725c6 --- /dev/null +++ b/loader/loader.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include "stdio.h" + +#include "io.h" +#include "microwatt_soc.h" +#include "console.h" +#include "elf64.h" + +#define DTB_ADDR 0x01000000UL +#define DTBIMAGE_ADDR 0x00500000UL + +#ifdef DEBUG +#define debug(...) printf(__VA_ARGS__) +#else +#define debug(...) do {} while(0) +#endif + +int _printf(const char *fmt, ...) +{ + int count; + char buffer[128]; + va_list ap; + + va_start(ap, fmt); + count = vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + puts(buffer); + return count; +} + +static inline void flush_cpu_icache(void) +{ + __asm__ volatile ("icbi 0,0; isync" : : : "memory"); +} + +static void print_hex(unsigned long val) +{ + int i, nibbles = sizeof(val)*2; + char buf[sizeof(val)*2+1]; + + for (i = nibbles-1; i >= 0; i--) { + buf[i] = (val & 0xf) + '0'; + if (buf[i] > '9') + buf[i] += ('a'-'0'-10); + val >>= 4; + } + buf[nibbles] = '\0'; + puts(buf); +} + +static void fl_read(void *dst, uint32_t offset, uint32_t size) +{ + memcpy(dst, (void *)(unsigned long)(SPI_FLASH_BASE + offset), size); +} + +static unsigned long boot_flash(unsigned int offset) +{ + Elf64_Ehdr ehdr; + Elf64_Phdr ph; + unsigned int i, poff, size, off; + void *addr; + + fl_read(&ehdr, offset, sizeof(ehdr)); + if (!IS_ELF(ehdr) || ehdr.e_ident[EI_CLASS] != ELFCLASS64) { + puts("Doesn't look like an elf64\n"); + return -1UL; + } + if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB || + ehdr.e_machine != EM_PPC64) { + puts("Not a ppc64le binary\n"); + return -1UL; + } + + poff = offset + ehdr.e_phoff; + for (i = 0; i < ehdr.e_phnum; i++) { + fl_read(&ph, poff, sizeof(ph)); + if (ph.p_type != PT_LOAD) + continue; + + /* XXX Add bound checking ! */ + size = ph.p_filesz; + addr = (void *)ph.p_vaddr; + off = offset + ph.p_offset; + debug("Copy segment %d (0x%x bytes) to %p\n", i, size, addr); + fl_read(addr, off, size); + poff += ehdr.e_phentsize; + } + + if (poff == offset + ehdr.e_phoff) { + puts("Did not find any loadable sections\n"); + return -1UL; + } + + debug("Found entry point: %x\n", ehdr.e_entry); + + flush_cpu_icache(); + return ehdr.e_entry; +} + +int main(void) +{ + unsigned long fl_off = 0; + potato_uart_init(); + unsigned long payload, dtb; + void __attribute__((noreturn)) (*enter_kernel)(unsigned long fdt, + unsigned long entry, + unsigned long of); + + + puts("\nMicrowatt Loader ("__DATE__" "__TIME__"\n\n"); + + writeq(SYS_REG_CTRL_DRAM_AT_0, SYSCON_BASE + SYS_REG_CTRL); + flush_cpu_icache(); + + puts("Load binaries into SDRAM and select option to start:\n\n"); + puts("vmlinux.bin and dtb:\n"); + puts(" mw_debug -b jtag stop load vmlinux.bin load microwatt.dtb 0x1000000 start\n"); + puts(" press 'l' to start'\n\n"); + + puts("dtbImage.microwatt:\n"); + puts(" mw_debug -b jtag stop load dtbImage.microwatt 0x500000 start\n"); + puts(" press 'w' to start'\n\n"); + + if (readq(SYSCON_BASE + SYS_REG_INFO) & SYS_REG_INFO_HAS_SPI_FLASH) { + unsigned long val = readq(SYSCON_BASE + SYS_REG_SPI_INFO); + fl_off = val & SYS_REG_SPI_INFO_FLASH_OFF_MASK; + + puts("Flash:\n"); + puts(" To boot a binary from flash, write it to "); print_hex(fl_off); puts("\n"); + puts(" press 'f' to start'\n\n"); + } + + while (1) { + switch (getchar()) { + case 'l': + payload = 0; + dtb = DTB_ADDR; + goto load; + case 'w': + payload = DTBIMAGE_ADDR; + goto load; + case 'f': + payload = boot_flash(fl_off); + if (payload == -1UL) + continue; + goto load; + default: + continue; + } + } + +load: + puts("Entering payload at "); print_hex(payload); puts("...\n"); + + enter_kernel = (void *)payload; + + enter_kernel(dtb, 0, 0); +} diff --git a/loader/powerpc.lds.S b/loader/powerpc.lds.S new file mode 100644 index 0000000..8b08d62 --- /dev/null +++ b/loader/powerpc.lds.S @@ -0,0 +1,20 @@ +#include + +#define BASE_ADDR BRAM_BASE + +SECTIONS +{ + . = BASE_ADDR; + .head : { + KEEP(*(.head)) + } + + .text : { *(.text*) *(.sfpr) *(.rodata*) } + .data : { *(.data*) } + .bss : { *(.bss*) } + + /DISCARD/ : { + *(.note.*) + *(.comment) + } +}