From d87a3c824d5aa91ba615c70784cf2a1574542d60 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 19 Sep 2019 20:35:17 +0200 Subject: [PATCH] ipq40xx: Add ipq-aux-loader This adds ipq-aux-loader from Sergey Sergeev It is small loader for legacy or LZMA compressed FIT images. This is needed to add support for Mikrotik RouterBoards as they expect a specific ELF image. This loader enables to use LZMA compressed FIT image which will decompress the kernel image and boot it. Signed-off-by: Sergey Sergeev Signed-off-by: Robert Marko (reorganzied patches) Signed-off-by: Alexander Couzens --- target/linux/ipq40xx/image/Makefile | 18 + .../ipq40xx/image/ipq-aux-loader/Makefile | 68 ++ .../ipq40xx/image/ipq-aux-loader/src/Makefile | 162 +++++ .../image/ipq-aux-loader/src/README.md | 6 + .../image/ipq-aux-loader/src/openwrt/Makefile | 68 ++ .../image/ipq-aux-loader/src/src/Debug.sh | 4 + .../image/ipq-aux-loader/src/src/LzmaDecode.c | 584 ++++++++++++++++++ .../image/ipq-aux-loader/src/src/board.c | 60 ++ .../image/ipq-aux-loader/src/src/cpu.c | 501 +++++++++++++++ .../image/ipq-aux-loader/src/src/fdt.c | 141 +++++ .../image/ipq-aux-loader/src/src/head.S | 83 +++ .../src/src/include/LzmaDecode.h | 113 ++++ .../src/src/include/LzmaTypes.h | 45 ++ .../image/ipq-aux-loader/src/src/include/io.h | 28 + .../ipq-aux-loader/src/src/include/iomap.h | 51 ++ .../ipq-aux-loader/src/src/include/printf.h | 24 + .../ipq-aux-loader/src/src/include/types.h | 26 + .../src/src/include/uimage/fdt.h | 77 +++ .../src/src/include/uimage/legacy.h | 42 ++ .../ipq-aux-loader/src/src/kernel-data.lds | 9 + .../image/ipq-aux-loader/src/src/loader.c | 233 +++++++ .../image/ipq-aux-loader/src/src/loader.lds | 20 + .../image/ipq-aux-loader/src/src/loader2.lds | 20 + .../image/ipq-aux-loader/src/src/lzma.c | 119 ++++ .../image/ipq-aux-loader/src/src/printf.c | 407 ++++++++++++ .../image/ipq-aux-loader/src/src/qcom_uart.c | 143 +++++ .../image/ipq-aux-loader/src/src/start.S | 69 +++ .../image/ipq-aux-loader/src/src/watchdog.c | 96 +++ .../ipq-aux-loader/src/ubi/loader.ubi.ini | 9 + 29 files changed, 3226 insertions(+) create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/Makefile create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/Makefile create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/README.md create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/openwrt/Makefile create mode 100755 target/linux/ipq40xx/image/ipq-aux-loader/src/src/Debug.sh create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/LzmaDecode.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/board.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/cpu.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/fdt.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/head.S create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/LzmaDecode.h create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/LzmaTypes.h create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/io.h create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/iomap.h create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/printf.h create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/types.h create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/uimage/fdt.h create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/uimage/legacy.h create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/kernel-data.lds create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader.lds create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader2.lds create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/lzma.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/printf.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/qcom_uart.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/start.S create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/src/watchdog.c create mode 100644 target/linux/ipq40xx/image/ipq-aux-loader/src/ubi/loader.ubi.ini diff --git a/target/linux/ipq40xx/image/Makefile b/target/linux/ipq40xx/image/Makefile index 0f03e1f429..ea0d733678 100644 --- a/target/linux/ipq40xx/image/Makefile +++ b/target/linux/ipq40xx/image/Makefile @@ -18,6 +18,24 @@ define Device/Default IMAGE/sysupgrade.bin/squashfs := endef +define Build/ipq-aux-loader-common + rm -rf $@.src + $(MAKE) -C ipq-aux-loader \ + PKG_BUILD_DIR="$@.src" \ + TARGET_DIR="$(dir $@)" LOADER_NAME="$(notdir $@)" \ + TEXT_BASE2="0x00000000" \ + BOARD="$(BOARDNAME)" PLATFORM="$(PLATFORM)" \ + BLOCKSIZE="$(BLOCKSIZE)" PAGESIZE="$(PAGESIZE)" \ + UART="$(AUX_LOADER_UART)" \ + $(1) LOADER_TYPE=$(2) compile loader.$(2) + mv "$@.$(2)" "$@" + rm -rf $@.src +endef + +define Build/ipq-aux-loader-kernel + $(call Build/ipq-aux-loader-common,LOADER_DATA="$@",$(1)) +endef + define Device/FitImage KERNEL_SUFFIX := -fit-uImage.itb KERNEL = kernel-bin | gzip | fit gzip $$(DTS_DIR)/$$(DEVICE_DTS).dtb diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/Makefile b/target/linux/ipq40xx/image/ipq-aux-loader/Makefile new file mode 100644 index 0000000000..5659b69194 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/Makefile @@ -0,0 +1,68 @@ +# +# Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards +# +# Copyright (C) 2019 Sergey Sergeev +# +# Some parts of this code was based on the OpenWrt specific lzma-loader +# for the Atheros AR7XXX/AR9XXX based boards: +# Copyright (C) 2011 Gabor Juhos +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# + +include $(TOPDIR)/rules.mk + +LOADER_NAME := $(basename $(notdir $(LOADER))) + +ifeq ($(TARGET_DIR),) +TARGET_DIR := $(KDIR) +endif + +LOADER_ELF := $(TARGET_DIR)/$(LOADER_NAME).elf +LOADER_UBI := $(TARGET_DIR)/$(LOADER_NAME).ubi + +PKG_NAME := ipq-aux-loader +PKG_BUILD_DIR := $(KDIR)/$(PKG_NAME) + +.PHONY : loader-compile loader.elf + +$(PKG_BUILD_DIR)/.prepared: + mkdir $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR) + touch $@ + +CPU_TYPE := UNKNOWN + +ifeq ($(PLATFORM),mikrotik-ipq4xxx) + CPU_TYPE := IPQ4XXX + TEXT_BASE := 0x84800000 + TEXT_BASE2 := 0x01100000 + FAT_SIZE_START := 5000000 + TEXT_BASE2_FAT := 0x00000000 +endif + +loader-compile: $(PKG_BUILD_DIR)/.prepared + $(MAKE) -C $(PKG_BUILD_DIR) CROSS_COMPILE="$(TARGET_CROSS)" \ + TEXT_BASE=$(TEXT_BASE) TEXT_BASE2=$(TEXT_BASE2) \ + FAT_SIZE_START=$(FAT_SIZE_START) \ + TEXT_BASE2_FAT=$(TEXT_BASE2_FAT) \ + CPU_TYPE=$(CPU_TYPE) KERNEL_IMAGE=$(LOADER_DATA) \ + BLOCKSIZE=$(BLOCKSIZE) PAGESIZE=$(PAGESIZE) \ + clean $(LOADER_TYPE) + +loader.elf: $(PKG_BUILD_DIR)/bin/loader.elf + $(CP) $< $(LOADER_ELF) + +loader.ubi: $(PKG_BUILD_DIR)/bin/loader.ubi + $(CP) $< $(LOADER_UBI) + +download: +prepare: $(PKG_BUILD_DIR)/.prepared +compile: loader-compile + +install: + +clean: + rm -rf $(PKG_BUILD_DIR) diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/Makefile b/target/linux/ipq40xx/image/ipq-aux-loader/src/Makefile new file mode 100644 index 0000000000..8c5b7ac17d --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/Makefile @@ -0,0 +1,162 @@ +# +# Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards +# +# Copyright (C) 2019 Sergey Sergeev +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# + +CPU_TYPE := IPQ4XXX +#+72M. realloc memory address +TEXT_BASE := 0x44800000 +#RouterBOOT auto realloc flag value address. Kernel size is limited < 6M ! +TEXT_BASE2 := 0x01100000 +#fat kernels start size 5M +FAT_SIZE_START := 5000000 +#for fat kernels <= 12M +TEXT_BASE2_FAT := 0x00000000 + +#ubi image vars +BLOCKSIZE := 256k +PAGESIZE := 4096 + +CC := $(CROSS_COMPILE)gcc +LD := $(CROSS_COMPILE)ld +OBJCOPY := $(CROSS_COMPILE)objcopy +OBJDUMP := $(CROSS_COMPILE)objdump + +BIN_FLAGS := -O binary -R .ARM.attributes -R .comment -R .debug.* -S + +CFLAGS = -D__KERNEL__ -DCONFIG_SYS_TEXT_BASE=$(TEXT_BASE) \ + -DCONFIG_ARM -D__ARM__ -fPIC -Wall -Wstrict-prototypes \ + -Wno-format-security -Wno-format-nonliteral \ + -fno-stack-protector -fstack-usage -pipe \ + -marm -mno-thumb-interwork -mabi=aapcs-linux -march=armv7-a \ + -mno-unaligned-access -fno-builtin -ffreestanding \ + -g -Os -fno-common -ffixed-r8 + +ifeq ($(CPU_TYPE),IPQ4XXX) + CFLAGS += -DCONFIG_IPQ4XXX +endif +ifeq ($(CPU_TYPE),IPQ806X) + CFLAGS += -DCONFIG_IPQ806X +endif + +#Com port number(for printf) +ifeq ($(UART),) + CFLAGS += -DUARTx_DM_BASE=UART1_DM_BASE +else +ifeq ($(UART),NONE) + CFLAGS += -DUARTx_DM_BASE=NONE +else + CFLAGS += -DUARTx_DM_BASE=UART$(UART)_DM_BASE +endif +endif + +#debug messages +ifeq ($(DEBUG),true) + CFLAGS += -DDEBUG +endif + +CFLAGS += -I./src/include + +ASFLAGS = $(CFLAGS) -D__ASSEMBLY__ + +LDFLAGS = +LDFLAGS_DATA = -r -b binary --oformat $(O_FORMAT) -o + +O_FORMAT = $(shell $(OBJDUMP) -i | head -2 | grep elf32) + +ldr := bin/loader +ldr-bin := bin/loader.bin +ldr-elf := bin/loader.elf +ldr-ubifs := bin/loader.ubifs +ldr-ubi := bin/loader.ubi + +tgs-h := io.h iomap.h LzmaDecode.h LzmaTypes.h printf.h types.h \ + uimage/fdt.h uimage/legacy.h +tgs-lds := kernel-data.lds loader2.lds loader.lds +tgs-o := start.o board.o cpu.o fdt.o loader.o lzma.o LzmaDecode.o \ + printf.o qcom_uart.o watchdog.o data.o +tgs2-o := head.o data2.o watchdog.o +tgs-h := $(tgs-h:%=src/include/%) +tgs-lds := $(tgs-lds:%=src/%) +tgs-o := $(tgs-o:%=objs/%) +tgs2-o := $(tgs2-o:%=objs/%) + +ifeq ($(KERNEL_IMAGE),) + KERNEL_IMAGE_FS = 0 +endif +ifeq ($(shell test -f $(KERNEL_IMAGE); echo $$?),0) + KERNEL_IMAGE_FS = $(shell stat -L -c %s $(KERNEL_IMAGE)) +else + KERNEL_IMAGE_FS = 0 +endif + +#Switching to use kernel size for fat images. +ifeq ($(shell test ${KERNEL_IMAGE_FS} -gt ${FAT_SIZE_START}; echo $$?),0) + TEXT_BASE2 = $(TEXT_BASE2_FAT) +endif + +#Create the necessary directories +$(shell mkdir -p objs maps bin) + +elf: $(ldr-elf) +ubi: $(ldr-ubi) +all: $(ldr-ubi) + +# Don't build dependencies, this may die if $(CC) isn't gcc +dep: + +install: + +$(tgs-lds): +$(tgs-h): + +$(tgs-o): $(tgs-lds) +$(tgs-o): $(tgs-h) + +# objs/qcom_uart.o objs/printf.o - add it to *.o list for debug +$(ldr-elf): $(tgs2-o) + @echo "Kernel size = ${KERNEL_IMAGE_FS}" + $(LD) $(LDFLAGS) -e _start -Ttext $(TEXT_BASE2) -o $(ldr-elf) \ + $(tgs2-o) -Map maps/loader2.map -T src/loader2.lds + +$(ldr-bin): $(ldr) + $(OBJCOPY) $(BIN_FLAGS) $< $@ + +$(ldr): $(tgs-o) + $(LD) $(LDFLAGS) -e _start -Ttext $(TEXT_BASE) \ + $(tgs-o) -Map maps/loader.map -T src/loader.lds -o $@ + +objs/%.o: src/%.c + $(CC) $(CFLAGS) -o $@ $< -c + +objs/%.o: src/%.S + $(CC) $(ASFLAGS) -o $@ $< -c + +objs/data2.o: $(ldr-bin) + $(LD) $(LDFLAGS_DATA) $@ $< + +objs/data.o: $(KERNEL_IMAGE) + $(LD) $(LDFLAGS_DATA) $@ $< -T src/kernel-data.lds + +$(ldr-ubi): $(ldr-ubifs) + $(STAGING_DIR_HOST)/bin/ubinize -p $(BLOCKSIZE:%k=%KiB) \ + -m $(PAGESIZE) -o $@ ubi/loader.ubi.ini + +$(ldr-ubifs): $(ldr-elf) + rm -Rf ubi/ubifs-rootdir + mkdir ubi/ubifs-rootdir + cp bin/loader.elf ubi/ubifs-rootdir/kernel + $(STAGING_DIR_HOST)/bin/mkfs.ubifs \ + -m $(PAGESIZE) -e 253952 -c 60 --compr=none \ + -r ubi/ubifs-rootdir $(ldr-ubifs) + rm -Rf ubi/ubifs-rootdir + +mrproper: clean + +clean: + rm -Rf bin/* objs/* maps/* ubi/ubifs-rootdir/ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/README.md b/target/linux/ipq40xx/image/ipq-aux-loader/src/README.md new file mode 100644 index 0000000000..7bdda955ab --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/README.md @@ -0,0 +1,6 @@ +# qcom-ipq-aux-loader + +This auxiliary bootloader was created to be able to run OpenWRT on devices of the Mikrotik company with is based on IPQ-4XXX and IPQ-806X series processors. +The thing is that a RouterBOOT is able to load only programs in ELF format and a OpenWRT kernel has a format of uImage Legacy or uImage FIT. + +This bootloader with a small fixes can potentially be used on any ARM system(V5 and higher). Dependence from hardware here is minimal. diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/openwrt/Makefile b/target/linux/ipq40xx/image/ipq-aux-loader/src/openwrt/Makefile new file mode 100644 index 0000000000..59e6622b05 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/openwrt/Makefile @@ -0,0 +1,68 @@ +# +# Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards +# +# Copyright (C) 2019 Sergey Sergeev +# +# Some parts of this code was based on the OpenWrt specific lzma-loader +# for the Atheros AR7XXX/AR9XXX based boards: +# Copyright (C) 2011 Gabor Juhos +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# + +include $(TOPDIR)/rules.mk + +LOADER_NAME := $(basename $(notdir $(LOADER))) + +ifeq ($(TARGET_DIR),) +TARGET_DIR := $(KDIR) +endif + +LOADER_ELF := $(TARGET_DIR)/$(LOADER_NAME).elf +LOADER_UBI := $(TARGET_DIR)/$(LOADER_NAME).ubi + +PKG_NAME := ipq-aux-loader +PKG_BUILD_DIR := $(KDIR)/$(PKG_NAME) + +.PHONY : loader-compile loader.elf + +$(PKG_BUILD_DIR)/.prepared: + mkdir $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR) + touch $@ + +CPU_TYPE := UNKNOWN + +ifeq ($(PLATFORM),mikrotik-ipq4xxx) + CPU_TYPE := IPQ4XXX + TEXT_BASE := 0x84800000 + TEXT_BASE2 := 0x01100000 + FAT_SIZE_START := 5000000 + TEXT_BASE2_FAT := 0x00000000 +endif + +loader-compile: $(PKG_BUILD_DIR)/.prepared + $(MAKE) -C $(PKG_BUILD_DIR) CROSS_COMPILE="$(TARGET_CROSS)" \ + TEXT_BASE=$(TEXT_BASE) TEXT_BASE2=$(TEXT_BASE2) \ + FAT_SIZE_START=$(FAT_SIZE_START) \ + TEXT_BASE2_FAT=$(TEXT_BASE2_FAT) \ + CPU_TYPE=$(CPU_TYPE) KERNEL_IMAGE=$(LOADER_DATA) \ + BLOCKSIZE=$(BLOCKSIZE) PAGESIZE=$(PAGESIZE) \ + clean $(LOADER_TYPE) + +loader.elf: $(PKG_BUILD_DIR)/bin/loader.elf + $(CP) $< $(LOADER_ELF) + +loader.ubi: $(PKG_BUILD_DIR)/bin/loader.ubi + $(CP) $< $(LOADER_UBI) + +download: +prepare: $(PKG_BUILD_DIR)/.prepared +compile: loader-compile + +install: + +clean: + rm -rf $(PKG_BUILD_DIR) diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/Debug.sh b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/Debug.sh new file mode 100755 index 0000000000..7d9b685e6d --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/Debug.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd ../ +./Debug.sh $@ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/LzmaDecode.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/LzmaDecode.c new file mode 100644 index 0000000000..cb8345377e --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/LzmaDecode.c @@ -0,0 +1,584 @@ +/* + LzmaDecode.c + LZMA Decoder (optimized for Speed version) + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this Code, expressly permits you to + statically or dynamically link your Code (or bind by name) to the + interfaces of this file without subjecting your linked Code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "LzmaDecode.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*Buffer++) + +#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \ + { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }} + +#ifdef _LZMA_IN_CB + +#define RC_TEST { if (Buffer == BufferLim) \ + { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \ + BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }} + +#define RC_INIT Buffer = BufferLim = 0; RC_INIT2 + +#else + +#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; } + +#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2 + +#endif + +#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; } + +#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound) +#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits; +#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits; + +#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \ + { UpdateBit0(p); mi <<= 1; A0; } else \ + { UpdateBit1(p); mi = (mi + mi) + 1; A1; } + +#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;) + +#define RangeDecoderBitTreeDecode(probs, numLevels, res) \ + { int i = numLevels; res = 1; \ + do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \ + res -= (1 << numLevels); } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size) +{ + unsigned char prop0; + if (size < LZMA_PROPERTIES_SIZE) + return LZMA_RESULT_DATA_ERROR; + prop0 = propsData[0]; + if (prop0 >= (9 * 5 * 5)) + return LZMA_RESULT_DATA_ERROR; + { + for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5)); + for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9); + propsRes->lc = prop0; + /* + unsigned char remainder = (unsigned char)(prop0 / 9); + propsRes->lc = prop0 % 9; + propsRes->pb = remainder / 5; + propsRes->lp = remainder % 5; + */ + } + + #ifdef _LZMA_OUT_READ + { + int i; + propsRes->DictionarySize = 0; + for (i = 0; i < 4; i++) + propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8); + if (propsRes->DictionarySize == 0) + propsRes->DictionarySize = 1; + } + #endif + return LZMA_RESULT_OK; +} + +#define kLzmaStreamWasFinishedId (-1) + +int LzmaDecode(CLzmaDecoderState *vs, + #ifdef _LZMA_IN_CB + ILzmaInCallback *InCallback, + #else + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + #endif + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed) +{ + CProb *p = vs->Probs; + SizeT nowPos = 0; + Byte previousByte = 0; + UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1; + UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1; + int lc = vs->Properties.lc; + + #ifdef _LZMA_OUT_READ + + UInt32 Range = vs->Range; + UInt32 Code = vs->Code; + #ifdef _LZMA_IN_CB + const Byte *Buffer = vs->Buffer; + const Byte *BufferLim = vs->BufferLim; + #else + const Byte *Buffer = inStream; + const Byte *BufferLim = inStream + inSize; + #endif + int state = vs->State; + UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; + int len = vs->RemainLen; + UInt32 globalPos = vs->GlobalPos; + UInt32 distanceLimit = vs->DistanceLimit; + + Byte *dictionary = vs->Dictionary; + UInt32 dictionarySize = vs->Properties.DictionarySize; + UInt32 dictionaryPos = vs->DictionaryPos; + + Byte tempDictionary[4]; + + #ifndef _LZMA_IN_CB + *inSizeProcessed = 0; + #endif + *outSizeProcessed = 0; + if (len == kLzmaStreamWasFinishedId) + return LZMA_RESULT_OK; + + if (dictionarySize == 0) + { + dictionary = tempDictionary; + dictionarySize = 1; + tempDictionary[0] = vs->TempDictionary[0]; + } + + if (len == kLzmaNeedInitId) + { + { + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + UInt32 i; + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + rep0 = rep1 = rep2 = rep3 = 1; + state = 0; + globalPos = 0; + distanceLimit = 0; + dictionaryPos = 0; + dictionary[dictionarySize - 1] = 0; + #ifdef _LZMA_IN_CB + RC_INIT; + #else + RC_INIT(inStream, inSize); + #endif + } + len = 0; + } + while(len != 0 && nowPos < outSize) + { + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + len--; + } + if (dictionaryPos == 0) + previousByte = dictionary[dictionarySize - 1]; + else + previousByte = dictionary[dictionaryPos - 1]; + + #else /* if !_LZMA_OUT_READ */ + + int state = 0; + UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + int len = 0; + const Byte *Buffer; + const Byte *BufferLim; + UInt32 Range; + UInt32 Code; + + #ifndef _LZMA_IN_CB + *inSizeProcessed = 0; + #endif + *outSizeProcessed = 0; + + { + UInt32 i; + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + } + + #ifdef _LZMA_IN_CB + RC_INIT; + #else + RC_INIT(inStream, inSize); + #endif + + #endif /* _LZMA_OUT_READ */ + + while(nowPos < outSize) + { + CProb *prob; + UInt32 bound; + int posState = (int)( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & posStateMask); + + prob = p + IsMatch + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + int symbol = 1; + UpdateBit0(prob) + prob = p + Literal + (LZMA_LIT_SIZE * + ((( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & literalPosMask) << lc) + (previousByte >> (8 - lc)))); + + if (state >= kNumLitStates) + { + int matchByte; + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + matchByte = dictionary[pos]; + #else + matchByte = outStream[nowPos - rep0]; + #endif + do + { + int bit; + CProb *probLit; + matchByte <<= 1; + bit = (matchByte & 0x100); + probLit = prob + 0x100 + bit + symbol; + RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break) + } + while (symbol < 0x100); + } + while (symbol < 0x100) + { + CProb *probLit = prob + symbol; + RC_GET_BIT(probLit, symbol) + } + previousByte = (Byte)symbol; + + outStream[nowPos++] = previousByte; + #ifdef _LZMA_OUT_READ + if (distanceLimit < dictionarySize) + distanceLimit++; + + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #endif + if (state < 4) state = 0; + else if (state < 10) state -= 3; + else state -= 6; + } + else + { + UpdateBit1(prob); + prob = p + IsRep + state; + IfBit0(prob) + { + UpdateBit0(prob); + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < kNumLitStates ? 0 : 3; + prob = p + LenCoder; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG0 + state; + IfBit0(prob) + { + UpdateBit0(prob); + prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + #ifdef _LZMA_OUT_READ + UInt32 pos; + #endif + UpdateBit0(prob); + + #ifdef _LZMA_OUT_READ + if (distanceLimit == 0) + #else + if (nowPos == 0) + #endif + return LZMA_RESULT_DATA_ERROR; + + state = state < kNumLitStates ? 9 : 11; + #ifdef _LZMA_OUT_READ + pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + outStream[nowPos++] = previousByte; + #ifdef _LZMA_OUT_READ + if (distanceLimit < dictionarySize) + distanceLimit++; + #endif + + continue; + } + else + { + UpdateBit1(prob); + } + } + else + { + UInt32 distance; + UpdateBit1(prob); + prob = p + IsRepG1 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep1; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG2 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep2; + } + else + { + UpdateBit1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = p + RepLenCoder; + } + { + int numBits, offset; + CProb *probLen = prob + LenChoice; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + numBits = kLenNumLowBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenChoice2; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + numBits = kLenNumMidBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + numBits = kLenNumHighBits; + } + } + RangeDecoderBitTreeDecode(probLen, numBits, len); + len += offset; + } + + if (state < 4) + { + int posSlot; + state += kNumLitStates; + prob = p + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + rep0 = (2 | ((UInt32)posSlot & 1)); + if (posSlot < kEndPosModelIndex) + { + rep0 <<= numDirectBits; + prob = p + SpecPos + rep0 - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + RC_NORMALIZE + Range >>= 1; + rep0 <<= 1; + if (Code >= Range) + { + Code -= Range; + rep0 |= 1; + } + } + while (--numDirectBits != 0); + prob = p + Align; + rep0 <<= kNumAlignBits; + numDirectBits = kNumAlignBits; + } + { + int i = 1; + int mi = 1; + do + { + CProb *prob3 = prob + mi; + RC_GET_BIT2(prob3, mi, ; , rep0 |= i); + i <<= 1; + } + while(--numDirectBits != 0); + } + } + else + rep0 = posSlot; + if (++rep0 == (UInt32)(0)) + { + /* it's for stream version */ + len = kLzmaStreamWasFinishedId; + break; + } + } + + len += kMatchMinLen; + #ifdef _LZMA_OUT_READ + if (rep0 > distanceLimit) + #else + if (rep0 > nowPos) + #endif + return LZMA_RESULT_DATA_ERROR; + + #ifdef _LZMA_OUT_READ + if (dictionarySize - distanceLimit > (UInt32)len) + distanceLimit += len; + else + distanceLimit = dictionarySize; + #endif + + do + { + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + len--; + outStream[nowPos++] = previousByte; + } + while(len != 0 && nowPos < outSize); + } + } + RC_NORMALIZE; + + #ifdef _LZMA_OUT_READ + vs->Range = Range; + vs->Code = Code; + vs->DictionaryPos = dictionaryPos; + vs->GlobalPos = globalPos + (UInt32)nowPos; + vs->DistanceLimit = distanceLimit; + vs->Reps[0] = rep0; + vs->Reps[1] = rep1; + vs->Reps[2] = rep2; + vs->Reps[3] = rep3; + vs->State = state; + vs->RemainLen = len; + vs->TempDictionary[0] = tempDictionary[0]; + #endif + + #ifdef _LZMA_IN_CB + vs->Buffer = Buffer; + vs->BufferLim = BufferLim; + #else + *inSizeProcessed = (SizeT)(Buffer - inStream); + #endif + *outSizeProcessed = nowPos; + return LZMA_RESULT_OK; +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/board.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/board.c new file mode 100644 index 0000000000..af922cb90b --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/board.c @@ -0,0 +1,60 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include + +unsigned long int ntohl(unsigned long int d){ + unsigned long int res = 0; + int a; + for(a = 0; a < 3; a++){ + res |= d & 0xFF; + res <<= 8; d >>= 8; + } + res |= d & 0xFF; + return res; +} + +void dump_mem(unsigned char *p, char *str){ + printf(" %s(0x%08X) = %02x %02x %02x %02x %02x %02x %02x %02x\n", + str, + p, + p[0] & 0xFF, p[1] & 0xFF, + p[2] & 0xFF, p[3] & 0xFF, + p[4] & 0xFF, p[5] & 0xFF, + p[6] & 0xFF, p[7] & 0xFF + ); +} + +void hang(void) +{ + printf("### ERROR ### Please RESET the board ###\n"); + for (;;); +} + +int raise(int signum) +{ + /* Needs for div/mod ops */ + printf("raise: Signal # %d caught\n", signum); + return 0; +} + +/* Replacement (=dummy) for GNU/Linux division-by zero handler */ +void __div0 (void) +{ + hang(); +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/cpu.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/cpu.c new file mode 100644 index 0000000000..5baf8f37e3 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/cpu.c @@ -0,0 +1,501 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include + +/* size: 4 * 4096. Start: TEXT_BASE + 40M. Try to limit to 128M. + !!! This region of memory should not be inside the caching area !!! +*/ +static u32 *page_table = (void*)(CONFIG_SYS_TEXT_BASE + 0x2800000); + +/* start and size of memory for D-Cache */ +static u32 bi_dram_0_start = 0; +static u32 bi_dram_0_size = 0; + +#define CR_C (1 << 2) /* Dcache enable */ +#define CR_I (1 << 12) /* Icache enable */ +#define CR_M (1 << 0) /* MMU enable */ + +#define CONFIG_NR_DRAM_BANKS 1 +#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) +#define CACHE_SETUP 0x1a +#else +#define CACHE_SETUP 0x1e +#endif + +#define isb() __asm__ __volatile__ ("" : : : "memory") +#define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t"); + +static void cp_delay (void) +{ + volatile int i; + + /* copro seems to need some delay between reading and writing */ + for (i = 0; i < 100; i++) + nop(); + asm volatile("" : : : "memory"); +} + +static inline unsigned int get_cr(void) +{ + unsigned int val; + asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc"); + return val; +} + +static inline void set_cr(unsigned int val) +{ + asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" + : : "r" (val) : "cc"); + isb(); +} + +/* Values for Ctype fields in CLIDR */ +#define ARMV7_CLIDR_CTYPE_NO_CACHE 0 +#define ARMV7_CLIDR_CTYPE_INSTRUCTION_ONLY 1 +#define ARMV7_CLIDR_CTYPE_DATA_ONLY 2 +#define ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA 3 +#define ARMV7_CLIDR_CTYPE_UNIFIED 4 + +#define ARMV7_DCACHE_INVAL_ALL 1 +#define ARMV7_DCACHE_CLEAN_INVAL_ALL 2 +#define ARMV7_DCACHE_INVAL_RANGE 3 +#define ARMV7_DCACHE_CLEAN_INVAL_RANGE 4 + +#define ARMV7_CSSELR_IND_DATA_UNIFIED 0 + +/* CCSIDR */ +#define CCSIDR_LINE_SIZE_OFFSET 0 +#define CCSIDR_LINE_SIZE_MASK 0x7 +#define CCSIDR_ASSOCIATIVITY_OFFSET 3 +#define CCSIDR_ASSOCIATIVITY_MASK (0x3FF << 3) +#define CCSIDR_NUM_SETS_OFFSET 13 +#define CCSIDR_NUM_SETS_MASK (0x7FFF << 13) + +/* + * CP15 Barrier instructions + * Please note that we have separate barrier instructions in ARMv7 + * However, we use the CP15 based instructtions because we use + * -march=armv5 in U-Boot + */ +#define CP15ISB asm volatile ("mcr p15, 0, %0, c7, c5, 4" : : "r" (0)) +#define CP15DSB asm volatile ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)) +#define CP15DMB asm volatile ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0)) + +static inline s32 log_2_n_round_up(u32 n) +{ + s32 log2n = -1; + u32 temp = n; + + while (temp) { + log2n++; + temp >>= 1; + } + + if (n & (n - 1)) + return log2n + 1; /* not power of 2 - round up */ + else + return log2n; /* power of 2 */ +} + +/* + * Write the level and type you want to Cache Size Selection Register(CSSELR) + * to get size details from Current Cache Size ID Register(CCSIDR) + */ +static void set_csselr(u32 level, u32 type) +{ u32 csselr = level << 1 | type; + + /* Write to Cache Size Selection Register(CSSELR) */ + asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr)); +} + +static u32 get_ccsidr(void) +{ + u32 ccsidr; + + /* Read current CP15 Cache Size ID Register */ + asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr)); + return ccsidr; +} + + +static u32 get_clidr(void) +{ + u32 clidr; + + /* Read current CP15 Cache Level ID Register */ + asm volatile ("mrc p15,1,%0,c0,c0,1" : "=r" (clidr)); + return clidr; +} + +static void v7_inval_dcache_level_setway(u32 level, u32 num_sets, + u32 num_ways, u32 way_shift, + u32 log2_line_len) +{ + int way, set, setway; + + /* + * For optimal assembly code: + * a. count down + * b. have bigger loop inside + */ + for (way = num_ways - 1; way >= 0 ; way--) { + for (set = num_sets - 1; set >= 0; set--) { + setway = (level << 1) | (set << log2_line_len) | + (way << way_shift); + /* Invalidate data/unified cache line by set/way */ + asm volatile (" mcr p15, 0, %0, c7, c6, 2" + : : "r" (setway)); + } + } + /* DSB to make sure the operation is complete */ + CP15DSB; +} + +static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets, + u32 num_ways, u32 way_shift, + u32 log2_line_len) +{ + int way, set, setway; + + /* + * For optimal assembly code: + * a. count down + * b. have bigger loop inside + */ + for (way = num_ways - 1; way >= 0 ; way--) { + for (set = num_sets - 1; set >= 0; set--) { + setway = (level << 1) | (set << log2_line_len) | + (way << way_shift); + /* + * Clean & Invalidate data/unified + * cache line by set/way + */ + asm volatile (" mcr p15, 0, %0, c7, c14, 2" + : : "r" (setway)); + } + } + /* DSB to make sure the operation is complete */ + CP15DSB; +} + +static void v7_maint_dcache_level_setway(u32 level, u32 operation) +{ + u32 ccsidr; + u32 num_sets, num_ways, log2_line_len, log2_num_ways; + u32 way_shift; + + set_csselr(level, ARMV7_CSSELR_IND_DATA_UNIFIED); + + ccsidr = get_ccsidr(); + + log2_line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >> + CCSIDR_LINE_SIZE_OFFSET) + 2; + /* Converting from words to bytes */ + log2_line_len += 2; + + num_ways = ((ccsidr & CCSIDR_ASSOCIATIVITY_MASK) >> + CCSIDR_ASSOCIATIVITY_OFFSET) + 1; + num_sets = ((ccsidr & CCSIDR_NUM_SETS_MASK) >> + CCSIDR_NUM_SETS_OFFSET) + 1; + /* + * According to ARMv7 ARM number of sets and number of ways need + * not be a power of 2 + */ + log2_num_ways = log_2_n_round_up(num_ways); + + way_shift = (32 - log2_num_ways); + if (operation == ARMV7_DCACHE_INVAL_ALL) { + v7_inval_dcache_level_setway(level, num_sets, num_ways, + way_shift, log2_line_len); + } else if (operation == ARMV7_DCACHE_CLEAN_INVAL_ALL) { + v7_clean_inval_dcache_level_setway(level, num_sets, num_ways, + way_shift, log2_line_len); + } +} + + +static void v7_maint_dcache_all(u32 operation) +{ + u32 level, cache_type, level_start_bit = 0; + + u32 clidr = get_clidr(); + + for (level = 0; level < 7; level++) { + cache_type = (clidr >> level_start_bit) & 0x7; + if ((cache_type == ARMV7_CLIDR_CTYPE_DATA_ONLY) || + (cache_type == ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA) || + (cache_type == ARMV7_CLIDR_CTYPE_UNIFIED)) + v7_maint_dcache_level_setway(level, operation); + level_start_bit += 3; + } +} + +void __v7_outer_cache_flush_all(void) +{ +} +void v7_outer_cache_flush_all(void) + __attribute__((weak, alias("__v7_outer_cache_flush_all"))); + +void __v7_outer_cache_disable(void) +{ +} +void v7_outer_cache_disable(void) + __attribute__((weak, alias("__v7_outer_cache_disable"))); + +void __v7_outer_cache_inval_all(void) +{ +} +void v7_outer_cache_inval_all(void) + __attribute__((weak, alias("__v7_outer_cache_inval_all"))); + +void set_l2_indirect_reg(u32 reg_addr, u32 val) +{ + + asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t" + "isb\n\t" + "mcr p15, 3, %[l2cpdr], c15, c0, 7\n\t" + "isb\n\t" + : + : [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val) + ); +} + +u32 get_l2_indirect_reg(u32 reg_addr) +{ + u32 val; + + asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t" + "isb\n\t" + "mrc p15, 3, %[l2cpdr], c15, c0, 7\n\t" + : [l2cpdr]"=r" (val) + : [l2cpselr]"r" (reg_addr) + ); + + return val; +} + +void clear_l2cache_err(void) +{ +#if defined CONFIG_IPQ806X +#define L2ESR_IND_ADDR (0x204) + unsigned int val; + + val = get_l2_indirect_reg(L2ESR_IND_ADDR); + set_l2_indirect_reg(L2ESR_IND_ADDR, val); +#endif +} + +void __arm_init_before_mmu(void) +{ +} +void arm_init_before_mmu(void) + __attribute__((weak, alias("__arm_init_before_mmu"))); + +void bi_dram_0_set_ranges(u32 start, u32 size){ + bi_dram_0_start = start; + bi_dram_0_size = size; +} + +static inline void dram_bank_mmu_setup(int bank) +{ + u32 i; + u32 skip = (u32)page_table >> 20; + for (i = bi_dram_0_start >> 20; + i < (bi_dram_0_start + bi_dram_0_size) >> 20; + i++) { + if(i == skip){ /* foolproof */ + continue; + } + page_table[i] = i << 20 | (3 << 10) | CACHE_SETUP; + } +} + +/* to activate the MMU we need to set up virtual memory: use 1M areas */ +static inline void mmu_setup(void) +{ + int i; + u32 reg; + + arm_init_before_mmu(); + /* Set up an identity-mapping for all 4GB, rw for everyone */ + for (i = 0; i < 4096; i++) + page_table[i] = i << 20 | (3 << 10) | 0x12; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + dram_bank_mmu_setup(i); + } + + /* Copy the page table address to cp15 */ + asm volatile("mcr p15, 0, %0, c2, c0, 0" + : : "r" (page_table) : "memory"); + /* Set the access control to all-supervisor */ + asm volatile("mcr p15, 0, %0, c3, c0, 0" + : : "r" (~0)); + /* and enable the mmu */ + reg = get_cr(); /* get control reg. */ + cp_delay(); + set_cr(reg | CR_M); +} + +static int mmu_enabled(void) +{ + return get_cr() & CR_M; +} + +/* cache_bit must be either CR_I or CR_C */ +static void cache_enable(uint32_t cache_bit) +{ + uint32_t reg; + + /* The data cache is not active unless the mmu is enabled too */ + if ((cache_bit == CR_C) && !mmu_enabled()) + mmu_setup(); + reg = get_cr(); /* get control reg. */ + cp_delay(); + set_cr(reg | cache_bit); +} + + +void enable_caches(void) +{ + cache_enable(CR_I); + cache_enable(CR_C); +} + +void invalidate_dcache_all(void) +{ + v7_maint_dcache_all(ARMV7_DCACHE_INVAL_ALL); + + v7_outer_cache_inval_all(); +} + +/* + * Performs a clean & invalidation of the entire data cache + * at all levels + */ +void flush_dcache_all(void) +{ + v7_maint_dcache_all(ARMV7_DCACHE_CLEAN_INVAL_ALL); + + v7_outer_cache_flush_all(); +} + +/* cache_bit must be either CR_I or CR_C */ +static void cache_disable(uint32_t cache_bit) +{ + uint32_t reg; + + reg = get_cr(); + cp_delay(); + + if (cache_bit == CR_C) { + /* if cache isn;t enabled no need to disable */ + if ((reg & CR_C) != CR_C) + return; + /* if disabling data cache, disable mmu too */ + cache_bit |= CR_M; + flush_dcache_all(); + } + set_cr(reg & ~cache_bit); +} + +void icache_disable(void) +{ + cache_disable(CR_I); +} + +void dcache_disable(void) +{ + cache_disable(CR_C); +} + +int disable_interrupts (void) +{ + unsigned long old,temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0xc0\n" + "msr cpsr_c, %1" + : "=r" (old), "=r" (temp) + : + : "memory"); + return (old & 0x80) == 0; +} + +/* Invalidate entire I-cache and branch predictor array */ +void invalidate_icache_all(void) +{ + /* + * Invalidate all instruction caches to PoU. + * Also flushes branch target cache. + */ + asm volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); + + /* Invalidate entire branch predictor array */ + asm volatile ("mcr p15, 0, %0, c7, c5, 6" : : "r" (0)); + + /* Full system DSB - make sure that the invalidation is complete */ + CP15DSB; + + /* ISB - make sure the instruction stream sees it */ + CP15ISB; +} + + +int cleanup_before_linux(void) +{ + /* + * this function is called just before we call linux + * it prepares the processor for linux + * + * we turn off caches etc ... + */ + disable_interrupts(); + + /* + * Turn off I-cache and invalidate it + */ + icache_disable(); + invalidate_icache_all(); + + /* + * turn off D-cache + * dcache_disable() in turn flushes the d-cache and disables MMU + */ + dcache_disable(); + v7_outer_cache_disable(); + + /* + * After D-cache is flushed and before it is disabled there may + * be some new valid entries brought into the cache. We are sure + * that these lines are not dirty and will not affect our execution. + * (because unwinding the call-stack and setting a bit in CP15 SCTRL + * is all we did during this. We have not pushed anything on to the + * stack. Neither have we affected any static data) + * So just invalidate the entire d-cache again to avoid coherency + * problems for kernel + */ + invalidate_dcache_all(); + + clear_l2cache_err(); + + /* Yes. I am a paranoid */ + /* Full system DSB - make sure that the invalidation is complete */ + CP15DSB; + /* ISB - make sure the instruction stream sees it */ + CP15ISB; + + return 0; +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/fdt.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/fdt.c new file mode 100644 index 0000000000..eca6fddcc0 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/fdt.c @@ -0,0 +1,141 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +#define FDT_FIRST_SUPPORTED_VERSION 0x10 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +unsigned long int ntohl(unsigned long int d); + +static int fdt_streq(char *s1, char *s2){ + for(; *s1 != '\0' && *s2 != '\0'; s1++, s2++) + if(*s1 != *s2) + return 0; + if(*s1 == '\0' && *s2 == '\0') + return 1; + return 0; +} + +static int fdt_strlen(char *s){ + int len = 0; + for(; *s != '\0'; s++, len++); + return len; +} + +/* + Checks fdt header variables for adequacy of values. +*/ +int fdt_check_header(void *data, u32 data_size){ + struct fdt_header *image = (void*)data; + u32 fdt_magic = ntohl(image->magic); + u32 totalsize = ntohl(image->totalsize); + u32 fdt_version = ntohl(image->version); + u32 off_dt_struct = ntohl(image->off_dt_struct); + u32 off_dt_strings = ntohl(image->off_dt_strings); + u32 fdt_last_comp_version = ntohl(image->last_comp_version); + if(fdt_magic != FDT_MAGIC) + return -1; + if(data_size < totalsize) + return -2; + if(fdt_version < FDT_FIRST_SUPPORTED_VERSION) + return -3; + if(fdt_last_comp_version > FDT_LAST_SUPPORTED_VERSION) + return -4; + if(off_dt_struct == 0 || off_dt_struct >= totalsize) + return -5; + if(off_dt_strings == 0 || off_dt_strings >= totalsize) + return -6; + return 0; +} + +/* + Returns: prop->data and prop->len for the prop with name prop_name + whose parent node name is node_name. +*/ +char *fdt_get_prop(void *data, char *node_name, char *prop_name, u32 *lenp_ret){ + struct fdt_header *image = (void*)data; + u32 off_dt_struct = ntohl(image->off_dt_struct); + u32 off_dt_strings = ntohl(image->off_dt_strings); + u32 totalsize = ntohl(image->totalsize); + u32 offset = 0; + u32 nextoffset = 0; + /* struct memory pointer */ + unsigned char *p = (void*)data + off_dt_struct; + /* props names are stored separately, in a strings heap */ + unsigned char *n = (void*)data + off_dt_strings; + /* data tail pointer */ + void *t = (void*)data + totalsize; + int is_node_found = 0; + struct fdt_property *fdt_prop; + char *cur_prop_name; + if(lenp_ret) + *lenp_ret = 0; + while(1){ + u32 *tagp = (void*)p + offset; + u32 *lenp; + u32 tag = ntohl(*tagp); + /* out of range protection */ + if((void*)tagp + sizeof(*tagp) >= t) + return NULL; /* offset is out of range ! */ + offset += FDT_TAGSIZE; /* skip tag var */ + if(tag == FDT_END) + break; /* end of tags list is reached */ + switch(tag){ + case FDT_BEGIN_NODE: /* node tag. is the parent node for props and node tags. */ + if(fdt_streq(node_name, (char*)(p + offset))) + is_node_found = 1; + else + is_node_found = 0; + /* skip name var */ + offset += fdt_strlen((char*)(p + offset)) + 1; + break; + case FDT_PROP: /* props of this node */ + lenp = (void*)p + offset; + if(is_node_found){ + /* skip-name offset, length and value */ + fdt_prop = (void*)p + offset - FDT_TAGSIZE; + /* out of range protection */ + if((void*)fdt_prop + sizeof(*fdt_prop) >= t) + return NULL; /* fdt_prop is out of range ! */ + cur_prop_name = (char *)(n + ntohl(fdt_prop->nameoff)); + /* out of range protection */ + if((void*)cur_prop_name >= t) + return NULL; /* nameoff is out of range ! */ + if(fdt_streq(prop_name, cur_prop_name)){ + if(lenp_ret) + *lenp_ret = ntohl(*lenp); + return fdt_prop->data; + } + } + offset += sizeof(struct fdt_property) - FDT_TAGSIZE + ntohl(*lenp); + break; + case FDT_END: /* end of tags list */ + case FDT_END_NODE: /* end of current node */ + is_node_found = 0; + case FDT_NOP: /* nop ? */ + break; + default: + return NULL; /* unknown tag */ + } + nextoffset = FDT_TAGALIGN(offset); + offset = nextoffset; + } + return NULL; /* return NULL on error */ +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/head.S b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/head.S new file mode 100644 index 0000000000..96d5d2462d --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/head.S @@ -0,0 +1,83 @@ +.globl _start +_start: + mov r7, pc @save REAL _start addr + sub r7, r7, #8 + + mov r6, r1 @save ARGS[1] to r6(ARCH value) + + @mov r0, r7 + @bl owl_debug + + ldr r0, =(CONFIG_SYS_TEXT_BASE) + mov r1, r7 @ real _start addr ! + mov r2, r6 @ARCH value + @bl owl_debug + bl relocate_code + +.globl _payload_data_begin_ofs +_payload_data_begin_ofs: + .word _payload_data_begin - _start + +.globl _payload_data_end_ofs +_payload_data_end_ofs: + .word _payload_data_end - _start + +/* + * void relocate_code (dst, REAL _start addr, ARCH) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + */ +.globl relocate_code +.align 4 +relocate_code: + mov r6, r0 /* save addr of destination */ + mov r5, r1 /* REAL _start addr */ + mov r4, r2 /* ARCH */ + + /* Set up the stack */ +stack_setup: + mov sp, r6 + + bl watchdog_resetup /* resetup watchdog timer */ + + ldr r0, _payload_data_begin_ofs + add r0, r0, r5 /* add in memory offset */ + mov r1, r6 /* r1 <- scratch for copy_loop */ + ldr r2, _payload_data_end_ofs + add r2, r2, r5 /* add in memory offset */ + + @bl owl_debug +copy_loop: + ldmia r0!, {r9-r10} /* copy from source address [r0] */ + stmia r1!, {r9-r10} /* copy to target address [r1] */ + cmp r0, r2 /* until source end address [r2] */ + blo copy_loop + +/* Bss reset will be made by the target called function(start.S) */ + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ +jump_2_ram: +/* + * If I-cache is enabled invalidate it. + * In our case(RouterBOOT) it should be turned off + * anyway, but let it be. + */ +#ifndef CONFIG_SYS_ICACHE_OFF + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ invalidate icache + mcr p15, 0, r0, c7, c10, 4 @ DSB + mcr p15, 0, r0, c7, c5, 4 @ ISB +#endif + ldr r0, =(CONFIG_SYS_TEXT_BASE) + mov r1, r5 /* REAL _start addr */ + mov r2, r4 /* ARCH */ + /* jump to it ... */ + mov pc, r0 + +.type relocate_code STT_FUNC +.size relocate_code, .-relocate_code diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/LzmaDecode.h b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/LzmaDecode.h new file mode 100644 index 0000000000..2870eeb9c9 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/LzmaDecode.h @@ -0,0 +1,113 @@ +/* + LzmaDecode.h + LZMA Decoder interface + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#ifndef __LZMADECODE_H +#define __LZMADECODE_H + +#include "LzmaTypes.h" + +/* #define _LZMA_IN_CB */ +/* Use callback for input data */ + +/* #define _LZMA_OUT_READ */ +/* Use read function for output data */ + +/* #define _LZMA_PROB32 */ +/* It can increase speed on some 32-bit CPUs, + but memory usage will be doubled in that case */ + +/* #define _LZMA_LOC_OPT */ +/* Enable local speed optimizations inside code */ + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 + +#ifdef _LZMA_IN_CB +typedef struct _ILzmaInCallback +{ + int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize); +} ILzmaInCallback; +#endif + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LZMA_PROPERTIES_SIZE 5 + +typedef struct _CLzmaProperties +{ + int lc; + int lp; + int pb; + #ifdef _LZMA_OUT_READ + UInt32 DictionarySize; + #endif +}CLzmaProperties; + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size); + +#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp))) + +#define kLzmaNeedInitId (-2) + +typedef struct _CLzmaDecoderState +{ + CLzmaProperties Properties; + CProb *Probs; + + #ifdef _LZMA_IN_CB + const unsigned char *Buffer; + const unsigned char *BufferLim; + #endif + + #ifdef _LZMA_OUT_READ + unsigned char *Dictionary; + UInt32 Range; + UInt32 Code; + UInt32 DictionaryPos; + UInt32 GlobalPos; + UInt32 DistanceLimit; + UInt32 Reps[4]; + int State; + int RemainLen; + unsigned char TempDictionary[4]; + #endif +} CLzmaDecoderState; + +#ifdef _LZMA_OUT_READ +#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; } +#endif + +int LzmaDecode(CLzmaDecoderState *vs, + #ifdef _LZMA_IN_CB + ILzmaInCallback *inCallback, + #else + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + #endif + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed); + +#endif diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/LzmaTypes.h b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/LzmaTypes.h new file mode 100644 index 0000000000..9c27290757 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/LzmaTypes.h @@ -0,0 +1,45 @@ +/* +LzmaTypes.h + +Types for LZMA Decoder + +This file written and distributed to public domain by Igor Pavlov. +This file is part of LZMA SDK 4.40 (2006-05-01) +*/ + +#ifndef __LZMATYPES_H +#define __LZMATYPES_H + +#ifndef _7ZIP_BYTE_DEFINED +#define _7ZIP_BYTE_DEFINED +typedef unsigned char Byte; +#endif + +#ifndef _7ZIP_UINT16_DEFINED +#define _7ZIP_UINT16_DEFINED +typedef unsigned short UInt16; +#endif + +#ifndef _7ZIP_UINT32_DEFINED +#define _7ZIP_UINT32_DEFINED +#ifdef _LZMA_UINT32_IS_ULONG +typedef unsigned long UInt32; +#else +typedef unsigned int UInt32; +#endif +#endif + +/* #define _LZMA_NO_SYSTEM_SIZE_T */ +/* You can use it, if you don't want */ + +#ifndef _7ZIP_SIZET_DEFINED +#define _7ZIP_SIZET_DEFINED +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +#include +typedef size_t SizeT; +#endif +#endif + +#endif diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/io.h b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/io.h new file mode 100644 index 0000000000..bf66fa08d7 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/io.h @@ -0,0 +1,28 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef __ASM_ARM_MINI_IO_H +#define __ASM_ARM_MINI_IO_H + +#define dmb() __asm__ __volatile__ ("" : : : "memory") +#define __iormb() dmb() +#define __iowmb() dmb() +#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v)) +#define __arch_getl(a) (*(volatile unsigned int *)(a)) + +#define writel(v,c) ({ unsigned int __v = v; __iowmb(); __arch_putl(__v,c); __v; }) +#define readl(c) ({ unsigned int __v = __arch_getl(c); __iormb(); __v; }) + +#endif /* __ASM_ARM_MINI_IO_H */ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/iomap.h b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/iomap.h new file mode 100644 index 0000000000..a9c74fa295 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/iomap.h @@ -0,0 +1,51 @@ +/* + * + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *     * Redistributions of source code must retain the above copyright + *       notice, this list of conditions and the following disclaimer. + *     * 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. + *     * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. + */ + +#ifndef _PLATFORM_IPQ40XX_IOMAP_H_ +#define _PLATFORM_IPQ40XX_IOMAP_H_ + +#ifdef CONFIG_IPQ4XXX +#define CLK_CTL_BASE 0x01800000 +#define GCNT_BASE 0x004a1000 +#define TIMER_BASE 0x0B021000 +#define UART2_DM_BASE 0x078b0000 +#define UART1_DM_BASE 0x078af000 +#define I2C0_BASE 0x078B7000 +#define TLMM_BASE 0x01000000 +#define GPIO_CONFIG_ADDR(x) (TLMM_BASE + (x)*0x1000) +#define GPIO_IN_OUT_ADDR(x) (TLMM_BASE + 0x4 + (x)*0x1000) + +#endif /* CONFIG_IPQ4XXX */ +#ifdef CONFIG_IPQ806X +#define UART7_DM_BASE 0x16640000 + +#endif /* CONFIG_IPQ806X */ + +#endif /* _PLATFORM_IPQ40XX_IOMAP_H_ */ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/printf.h b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/printf.h new file mode 100644 index 0000000000..3cf3670839 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/printf.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _printf_h_ +#define _printf_h_ + +#ifdef DEBUG +#define debug(format, args...) printf(format, ##args) +#else +#define debug(...) +#endif /* DEBUG */ + +#include +int printf(const char *fmt, ...); + +#endif /* _printf_h_ */ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/types.h b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/types.h new file mode 100644 index 0000000000..c30b930b22 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/types.h @@ -0,0 +1,26 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef __TYPES_MINI_H +#define __TYPES_MINI_H + +typedef unsigned long long uint64_t; +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +typedef unsigned long u32; +typedef long s32; +typedef unsigned char u8; +typedef unsigned short u16; + +typedef unsigned long ulong; + +#define NULL 0 + +#endif /* __TYPES_MINI_H */ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/uimage/fdt.h b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/uimage/fdt.h new file mode 100644 index 0000000000..2a24650258 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/uimage/fdt.h @@ -0,0 +1,77 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef _FDT_H +#define _FDT_H + +#ifndef __ASSEMBLY__ + +struct fdt_header { + uint32_t magic; /* magic word FDT_MAGIC */ + uint32_t totalsize; /* total size of DT block */ + uint32_t off_dt_struct; /* offset to structure */ + uint32_t off_dt_strings; /* offset to strings */ + uint32_t off_mem_rsvmap; /* offset to memory reserve map */ + uint32_t version; /* format version */ + uint32_t last_comp_version; /* last compatible version */ + + /* version 2 fields below */ + uint32_t boot_cpuid_phys; /* Which physical CPU id we're + booting on */ + /* version 3 fields below */ + uint32_t size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + uint32_t size_dt_struct; /* size of the structure block */ +}; + +struct fdt_reserve_entry { + uint64_t address; + uint64_t size; +}; + +struct fdt_node_header { + uint32_t tag; + char name[0]; +}; + +struct fdt_property { + uint32_t tag; + uint32_t len; + uint32_t nameoff; + char data[0]; +}; + +#endif /* !__ASSEMBLY */ + +#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ +#define FDT_TAGSIZE sizeof(uint32_t) + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, + size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 + +#define FDT_V1_SIZE (7*sizeof(uint32_t)) +#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t)) +#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t)) +#define FDT_V16_SIZE FDT_V3_SIZE +#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t)) + +/* adding a ramdisk needs 0x44 bytes in version 2008.10 */ +#define FDT_RAMDISK_OVERHEAD 0x80 +#endif /* _FDT_H */ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/uimage/legacy.h b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/uimage/legacy.h new file mode 100644 index 0000000000..b252672806 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/include/uimage/legacy.h @@ -0,0 +1,42 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef __LEGACY_H__ +#define __LEGACY_H__ + +#define LEGACY_IH_MAGIC 0x27051956 /* LEGACY Magic Number */ +#define FIT_IH_MAGIC 0xd00dfeed /* FIT Magic Number */ +#define LEGACY_IH_NMLEN 32 /* LEGACY Name Length */ + +/* + * Legacy format LEGACY header, + * all data in network byte order (aka natural aka bigendian). + */ +typedef struct legacy_image_header { + uint32_t ih_magic; /* LEGACY Header Magic Number */ + uint32_t ih_hcrc; /* LEGACY Header CRC Checksum */ + uint32_t ih_time; /* LEGACY Creation Timestamp */ + uint32_t ih_size; /* LEGACY Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* LEGACY Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* LEGACY Type */ + uint8_t ih_comp; /* Compression Type */ + uint8_t ih_name[LEGACY_IH_NMLEN]; /* LEGACY Name */ +} legacy_image_header_t; + +#endif /* __LEGACY_H__ */ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/kernel-data.lds b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/kernel-data.lds new file mode 100644 index 0000000000..0b2c8ce757 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/kernel-data.lds @@ -0,0 +1,9 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +SECTIONS { + .data.kernel : { + _kernel_data_start = .; + *(.data) + _kernel_data_end = .; + } +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader.c new file mode 100644 index 0000000000..b44abb0967 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader.c @@ -0,0 +1,233 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some parts of this code was based on the OpenWrt specific lzma-loader + * for the Atheros AR7XXX/AR9XXX based boards: + * Copyright (C) 2011 Gabor Juhos + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#define IPQ_ARCH 4200 + +/* beyond the image end, size not known in advance */ +extern unsigned char workspace[]; +u32 arch = 0; +void (*kernel_entry)(int zero, int arch, unsigned int params); +u32 kernel_p0 = 0; +u32 kernel_p1 = IPQ_ARCH; +u32 kernel_p2 = NULL; + +/* base stack poinet - 16 mb. stack grows left! + boot_params(12b)<--16M of stack--TEXT_BASE,_start--END of program + !!! TODO: replace to like workspace static mem region !!! +*/ +//u32 *boot_params = (void*)(CONFIG_SYS_TEXT_BASE - 0x100000C); + +void bi_dram_0_set_ranges(u32, u32); +int cleanup_before_linux(void); +void enable_caches(void); +extern u32 owl_get_sp(void); +void serial_putc(char c); +void watchdog_setup(int); +unsigned long int ntohl(unsigned long int); +void dump_mem(unsigned char *, char *); +void reset_cpu(ulong); +int fdt_check_header(void *, u32); +char *fdt_get_prop(void *, char *, char *, u32 *); +int lzma_gogogo(void *, void *, u32, u32 *); + +void my_memcpy(void *dst, const void *src, u32 n){ + const void *end = src + n; + for(; src + 4 <= end; src += 4, dst += 4) + *((u32*)dst) = *((u32*)src); + for(; src < end; src++, dst++) + *((u8*)dst) = *((u8*)src); +} + +int handle_legacy_header(void *_kernel_data_start, u32 kern_image_len){ + legacy_image_header_t *image = (void*)_kernel_data_start; + void *kernel_load = (void*)ntohl(image->ih_load); + void *kernel_ep = (void*)ntohl(image->ih_ep); + void *src = (void*)_kernel_data_start + sizeof(legacy_image_header_t); + void *dst = kernel_load; + /* compiler optimization barrier needed for GCC >= 3.4 */ + __asm__ __volatile__("": : :"memory"); + + u32 kernel_body_len = ntohl(image->ih_size); + + debug(" size = %u\n", kernel_body_len); + debug(" name = '%s'\n", (char*)image->ih_name); + debug(" load = 0x%08x\n", kernel_load); + debug(" ep = 0x%08x\n", kernel_ep); + + //check ih_size for adequate value + if(kern_image_len - sizeof(legacy_image_header_t) < kernel_body_len){ + printf("\n"); + printf("Error ! Kernel sizes mismath detected !\n"); + printf(" details: %d - %d < %d !\n", kern_image_len, + sizeof(legacy_image_header_t), kernel_body_len); + return -99; + } + debug("\n"); + printf("Copy kernel..."); + my_memcpy(dst, src, kernel_body_len); + printf("Done\n"); + kernel_entry = kernel_ep; + return 0; +} + +/* FIT - Flattened Image Tree. + ! Not to be confused with an FDT - Flattened Device Tree ! + ! MAGIC of FIT == ~MAGIC of FDT ! +*/ +#define FIT_KERNEL_NODE_NAME "kernel@1" +#define FIT_DTB_NODE_NAME "fdt@1" +int handle_fit_header(void *_kernel_data_start, u32 kern_image_len){ + void *data = (void*)_kernel_data_start; + void *kernel_load = NULL; + void *kernel_ep = NULL; + char *kernel_name = NULL; + char *kernel_compr = NULL; + void *dtb_data = NULL; //device tree blob + void *dtb_dst = (void*)workspace; + u32 dtb_body_len = 0; + u32 kernel_body_len = 0; + u32 kernel_uncompr_size = 0; + void *src = NULL; + void *dst = NULL; + char *tmp_c; + int ret; + /* compiler optimization barrier needed for GCC >= 3.4 */ + __asm__ __volatile__("": : :"memory"); + /* Do FDT header base checks */ + ret = fdt_check_header(data, kern_image_len); + if(ret){ + printf("Error ! FDT header is corrupted! check_ret = %d\n", ret); + return -99; + } + if(!(kernel_name = fdt_get_prop(data, FIT_KERNEL_NODE_NAME, "description", NULL))) + return -98; + if(!(tmp_c = fdt_get_prop(data, FIT_KERNEL_NODE_NAME, "load", NULL))) + return -97; + kernel_load = (void*)ntohl(*(u32*)tmp_c); + dst = kernel_load; + if(!(tmp_c = fdt_get_prop(data, FIT_KERNEL_NODE_NAME, "entry", NULL))) + return -96; + kernel_ep = (void*)ntohl(*(u32*)tmp_c); + if(!(src = fdt_get_prop(data, FIT_KERNEL_NODE_NAME, "data", &kernel_body_len))) + return -95; + if(!(kernel_compr = fdt_get_prop(data, FIT_KERNEL_NODE_NAME, "compression", NULL))) + return -94; + if(!(dtb_data = fdt_get_prop(data, FIT_DTB_NODE_NAME, "data", &dtb_body_len))) + return -93; + + debug(" size = %u\n", kernel_body_len); + debug(" name = '%s'\n", kernel_name); + debug(" load = 0x%08x\n", kernel_load); + debug(" ep = 0x%08x\n", kernel_ep); + debug(" compr = %s\n", kernel_compr); + + //check kernel@1->data size for adequate value + if(kernel_body_len >= kern_image_len){ + printf("\n"); + printf("Error ! Kernel sizes mismath detected !\n"); + printf(" details: %d >= %d !\n", kernel_body_len, kern_image_len); + return -99; + } + debug("\n"); + printf("Extracting LZMA kernel..."); + watchdog_setup(30); + /* without this all cpu operations is very very slow ! */ + if(dst < src){ + /* setup D-Cache ranges. our loader head + 32M */ + bi_dram_0_set_ranges((u32)dst, (u32)(src - dst + (void*)0x2000000)); + enable_caches(); /* Enable I and D caches ONLY for LZMA op */ + lzma_gogogo(dst, src, kernel_body_len, &kernel_uncompr_size); + } + cleanup_before_linux(); /* Disable I and D caches */ + + /* prepare device tree blob data for copy after kernel */ + my_memcpy(dtb_dst, dtb_data, dtb_body_len); + //dump_mem(workspace, "dtb_data:"); + + kernel_entry = kernel_ep; + kernel_p2 = (u32)dtb_dst; + return 0; +} + +void loader_main(u32 head_text_base, u32 _arch) +{ + extern char _kernel_data_start[]; + extern char _kernel_data_end[]; + u32 kern_image_len = _kernel_data_end - _kernel_data_start; + uint32_t *_magic = (void*)_kernel_data_start; + u32 magic; + int ret = -100; + + /* compiler optimization barrier needed for GCC >= 3.4 */ + __asm__ __volatile__("": : :"memory"); + + printf("\n"); + printf("OpenWrt kernel loader for Qualcomm IPQ-4XXX/IPQ-806X\n"); + printf("Copyright (C) 2019 Sergey Sergeev \n"); + + debug("\n"); + debug(" head loader TEXT_BASE = 0x%08X\n", head_text_base); + debug("kernel loader TEXT_BASE = 0x%08X\n", CONFIG_SYS_TEXT_BASE); + //printf("kernel_data_start = 0x%08X\n", _kernel_data_start); + //printf("kernel_data_end = 0x%08X\n", _kernel_data_end); + printf("\n"); + + //watchdog_setup(5); for(;;); + //reset_cpu(0); + + arch = _arch; + kernel_p1 = arch; + debug("ARCH = %d\n", arch); + if(arch != IPQ_ARCH){ + printf("Critical alert ! ARCH mismatch: %u vs %u\n", arch, IPQ_ARCH); + watchdog_setup(5); for(;;); + } + magic = ntohl(*_magic); + debug("Kernel image header:\n"); + switch(magic){ + case LEGACY_IH_MAGIC: + debug(" magic = 0x%x, Legacy uImage\n", magic); + ret = handle_legacy_header(_kernel_data_start, kern_image_len); + break; + case FIT_IH_MAGIC: + debug(" magic = 0x%x, FIT uImage\n", magic); + ret = handle_fit_header(_kernel_data_start, kern_image_len); + break; + default: + printf(" magic = 0x%x, UNKNOWN !!!\n", magic); + } + if(ret){ + printf("\n"); + printf("Op ret = %d\n", ret); + printf("Auto reboot in 5 sec\n"); + watchdog_setup(5); for(;;); + } + + printf("Starting kernel at 0x%08x\n", kernel_entry); + printf("\n"); + cleanup_before_linux(); + kernel_entry(kernel_p0, kernel_p1, kernel_p2); + reset_cpu(0); +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader.lds b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader.lds new file mode 100644 index 0000000000..968585fa8d --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader.lds @@ -0,0 +1,20 @@ +OUTPUT_ARCH(arm) +SECTIONS { + .text : { + _start = .; + *(.text) + *(.text.*) + *(.data) + *(.data.*) + } + . = ALIGN(32); + _bss_start = .; + .bss : { + *(.bss) + *(.bss.*) + } + . = ALIGN(32); + _bss_end = .; + . = ALIGN(32); + workspace = .; +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader2.lds b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader2.lds new file mode 100644 index 0000000000..ea5955c092 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/loader2.lds @@ -0,0 +1,20 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +SECTIONS { + .text : { + _start = .; + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + } + + . = ALIGN(32); + .data : { + _payload_data_begin = .; + *(.data) + _payload_data_end = .; + *(.data.*) + . = . + 524288; /* workaround for buggy bootloaders */ + } +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/lzma.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/lzma.c new file mode 100644 index 0000000000..7312b16464 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/lzma.c @@ -0,0 +1,119 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some parts of this code was based on the OpenWrt specific lzma-loader + * for the Atheros AR7XXX/AR9XXX based boards: + * Copyright (C) 2011 Gabor Juhos + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include + +/* beyond the image end, size not known in advance */ +extern unsigned char workspace[]; + +static CLzmaDecoderState lzma_state; +static unsigned char *lzma_data; +static unsigned long lzma_datasize; +static unsigned long lzma_outsize; +static unsigned long kernel_la; + +static void lzma_init_data(void *_kernel_la, void *_lzma_data, u32 _lzma_datasize) +{ + kernel_la = (unsigned long)_kernel_la; + lzma_data = _lzma_data; + lzma_datasize = _lzma_datasize; +} + +static __inline__ unsigned char lzma_get_byte(void) +{ + unsigned char c; + + lzma_datasize--; + c = *lzma_data++; + + return c; +} + +static int lzma_init_props(u32 *lzma_outsize_ret) +{ + unsigned char props[LZMA_PROPERTIES_SIZE]; + int res; + int i; + + /* read lzma properties */ + for (i = 0; i < LZMA_PROPERTIES_SIZE; i++) + props[i] = lzma_get_byte(); + + /* read the lower half of uncompressed size in the header */ + lzma_outsize = ((SizeT) lzma_get_byte()) + + ((SizeT) lzma_get_byte() << 8) + + ((SizeT) lzma_get_byte() << 16) + + ((SizeT) lzma_get_byte() << 24); + + if(lzma_outsize_ret) + *lzma_outsize_ret = lzma_outsize; + /* skip rest of the header (upper half of uncompressed size) */ + for (i = 0; i < 4; i++) + lzma_get_byte(); + + res = LzmaDecodeProperties(&lzma_state.Properties, props, + LZMA_PROPERTIES_SIZE); + return res; +} + +static int lzma_decompress(unsigned char *outStream) +{ + SizeT ip, op; + int ret; + + lzma_state.Probs = (CProb *) workspace; + + ret = LzmaDecode(&lzma_state, lzma_data, lzma_datasize, &ip, outStream, + lzma_outsize, &op); + + if (ret != LZMA_RESULT_OK) { + int i; + + printf("LzmaDecode error %d at %08x, osize:%d ip:%d op:%d\n", + ret, lzma_data + ip, lzma_outsize, ip, op); + + for (i = 0; i < 16; i++) + printf("%02x ", lzma_data[ip + i]); + + printf("\n"); + } + return ret; +} + +int lzma_gogogo(void *_kernel_la, void *_lzma_data, u32 _lzma_datasize, u32 *lzma_outsize_ret){ + int res; + lzma_init_data(_kernel_la, _lzma_data, _lzma_datasize); + res = lzma_init_props(lzma_outsize_ret); + if (res != LZMA_RESULT_OK) { + printf("Incorrect LZMA stream properties!\n"); + return -1; + } + res = lzma_decompress((unsigned char *) kernel_la); + if (res != LZMA_RESULT_OK) { + printf("failed, "); + switch (res) { + case LZMA_RESULT_DATA_ERROR: + printf("data error!\n"); + break; + default: + printf("unknown error %d!\n", res); + } + return -2; + } else { + printf("Done\n"); + } + return 0; +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/printf.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/printf.c new file mode 100644 index 0000000000..fccedb5406 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/printf.c @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Copyright (C) 2019 Sergey Sergeev + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include + +extern void serial_putc(char c); + +/* this is the maximum width for a variable */ +#define LP_MAX_BUF 256 + +/* macros */ +#define IsDigit(x) ( ((x) >= '0') && ((x) <= '9') ) +#define Ctod(x) ( (x) - '0') + +/* forward declaration */ +static int PrintChar(char *, char, int, int); +static int PrintString(char *, char *, int, int); +static int PrintNum(char *, unsigned int, int, int, int, int, char, int); + +/* private variable */ +static const char theFatalMsg[] = "fatal error in lp_Print!"; + +/* -*- + * A low level printf() function. + */ +static void +lp_Print(void (*output)(void *, char *, int), + void * arg, + char *fmt, + va_list ap) +{ + +#define OUTPUT(arg, s, l) \ + { if (((l) < 0) || ((l) > LP_MAX_BUF)) { \ + (*output)(arg, (char*)theFatalMsg, sizeof(theFatalMsg)-1); for(;;); \ + } else { \ + (*output)(arg, s, l); \ + } \ + } + + char buf[LP_MAX_BUF]; + + char c; + char *s; + long int num; + + int longFlag; + int negFlag; + int width; + int prec; + int ladjust; + char padc; + + int length; + + for(;;) { + { + /* scan for the next '%' */ + char *fmtStart = fmt; + while ( (*fmt != '\0') && (*fmt != '%')) { + fmt ++; + } + + /* flush the string found so far */ + OUTPUT(arg, fmtStart, fmt-fmtStart); + + /* are we hitting the end? */ + if (*fmt == '\0') break; + } + + /* we found a '%' */ + fmt ++; + + /* check for long */ + if (*fmt == 'l') { + longFlag = 1; + fmt ++; + } else { + longFlag = 0; + } + + /* check for other prefixes */ + width = 0; + prec = -1; + ladjust = 0; + padc = ' '; + + if (*fmt == '-') { + ladjust = 1; + fmt ++; + } + + if (*fmt == '0') { + padc = '0'; + fmt++; + } + + if (IsDigit(*fmt)) { + while (IsDigit(*fmt)) { + width = 10 * width + Ctod(*fmt++); + } + } + + if (*fmt == '.') { + fmt ++; + if (IsDigit(*fmt)) { + prec = 0; + while (IsDigit(*fmt)) { + prec = prec*10 + Ctod(*fmt++); + } + } + } + + + /* check format flag */ + negFlag = 0; + switch (*fmt) { + case 'b': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 2, 0, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'd': + case 'D': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + if (num < 0) { + num = - num; + negFlag = 1; + } + length = PrintNum(buf, num, 10, negFlag, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'o': + case 'O': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 8, 0, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'u': + case 'U': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 10, 0, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'x': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 16, 0, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'X': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 16, 0, width, ladjust, padc, 1); + OUTPUT(arg, buf, length); + break; + + case 'c': + c = (char)va_arg(ap, int); + length = PrintChar(buf, c, width, ladjust); + OUTPUT(arg, buf, length); + break; + + case 's': + s = (char*)va_arg(ap, char *); + length = PrintString(buf, s, width, ladjust); + OUTPUT(arg, buf, length); + break; + + case '\0': + fmt --; + break; + + default: + /* output this char as it is */ + OUTPUT(arg, fmt, 1); + } /* switch (*fmt) */ + + fmt ++; + } /* for(;;) */ + + /* special termination call */ + OUTPUT(arg, "\0", 1); +} + + +/* --------------- local help functions --------------------- */ +static int +PrintChar(char * buf, char c, int length, int ladjust) +{ + int i; + + if (length < 1) length = 1; + if (ladjust) { + *buf = c; + for (i=1; i< length; i++) buf[i] = ' '; + } else { + for (i=0; i< length-1; i++) buf[i] = ' '; + buf[length - 1] = c; + } + return length; +} + +static int +PrintString(char * buf, char* s, int length, int ladjust) +{ + int i; + int len=0; + char* s1 = s; + while (*s1++) len++; + if (length < len) length = len; + + if (ladjust) { + for (i=0; i< len; i++) buf[i] = s[i]; + for (i=len; i< length; i++) buf[i] = ' '; + } else { + for (i=0; i< length-len; i++) buf[i] = ' '; + for (i=length-len; i < length; i++) buf[i] = s[i-length+len]; + } + return length; +} + +#define wr2p(tmp){ \ + iters_count++; \ + if((tmp) <= 9){ \ + *p++ = '0' + (tmp); \ + }else if(upcase) { \ + *p++ = 'A' + (tmp) - 10; \ + }else{ \ + *p++ = 'a' + (tmp) - 10; \ + } \ +} + +/* 32 bit DEC unsigned int convert to string */ +/* https://stackoverflow.com/questions/33203044/number-to-digits-without-using-strings-nor-division-by-10 */ +static int my_dec2str(char *p, unsigned int A, int upcase){ + unsigned R, N; + unsigned long long Q; /* 32 bit Q can be owerflow, so use 64 bit */ + int iters_count = 0; + if(A < 10){ + wr2p(A); + return iters_count; + } + if(A == 10){ + wr2p(0); + wr2p(1); + return iters_count; + } + while(A > 0){ + Q = (((unsigned long long)A >> 1LLU) + (unsigned long long)A) >> 1LLU; + Q = ((Q >> 4LLU) + Q); + Q = ((Q >> 8LLU) + Q); + Q = ((Q >> 16LLU) + Q) >> 3LLU; + /* either Q = A / 10 or Q + 1 = A / 10 for all 32-bit unsigned A */ + R = A - 10 * Q; + N = Q; + if(R > 9){ + R = A - 10 * (Q + 1); + N = Q + 1; + } + A = N; + //printf("R = %u, Q = %u\n", R, Q); + wr2p(R); + } + return iters_count; +} + +/* 32 bit HEX unsigned int convert to string */ +static int my_hex2str(char *p, unsigned int A, int upcase){ + int iters_count = 0; + if(A < 16){ + wr2p(A); + return iters_count; + } + while(A > 0){ + wr2p(A & 0xF); + //printf("%d\n", A & 0xF); + A >>= 4; + } + return iters_count; +} + + +static int +PrintNum(char * buf, unsigned int u, int base, int negFlag, + int length, int ladjust, char padc, int upcase) +{ + /* algorithm : + * 1. prints the number from left to right in reverse form. + * 2. fill the remaining spaces with padc if length is longer than + * the actual length + * TRICKY : if left adjusted, no "0" padding. + * if negtive, insert "0" padding between "0" and number. + * 3. if (!ladjust) we reverse the whole string including paddings + * 4. otherwise we only reverse the actual string representing the num. + */ + + int actualLength =0; + char *p = buf; + int i; + + if(base == 10) + p += my_dec2str(p, u, upcase); + if(base == 16) + p += my_hex2str(p, u, upcase); + + if (negFlag) { + *p++ = '-'; + } + + /* figure out actual length and adjust the maximum length */ + actualLength = p - buf; + if (length < actualLength) length = actualLength; + + /* add padding */ + if (ladjust) { + padc = ' '; + } + if (negFlag && !ladjust && (padc == '0')) { + for (i = actualLength-1; i< length-1; i++) buf[i] = padc; + buf[length -1] = '-'; + } else { + for (i = actualLength; i< length; i++) buf[i] = padc; + } + + + /* prepare to reverse the string */ + { + int begin = 0; + int end; + if (ladjust) { + end = actualLength - 1; + } else { + end = length -1; + } + + while (end > begin) { + char tmp = buf[begin]; + buf[begin] = buf[end]; + buf[end] = tmp; + begin ++; + end --; + } + } + + /* adjust the string pointer */ + return length; +} + +static void printf_output(void *arg, char *s, int l) +{ + int i; + + // special termination call + if ((l==1) && (s[0] == '\0')) return; + + for (i=0; i< l; i++) { + serial_putc(s[i]); + if (s[i] == '\n') serial_putc('\r'); + } +} + +int printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + lp_Print(printf_output, 0, (char*)fmt, ap); + va_end(ap); + return 0; +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/qcom_uart.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/qcom_uart.c new file mode 100644 index 0000000000..89123acaa8 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/qcom_uart.c @@ -0,0 +1,143 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include + +/* Number of characters for Transmission */ +#define MSM_BOOT_UART_DM_NO_CHARS_FOR_TX(base) ((base) + 0x040) + +/*UART General Command */ +#define MSM_BOOT_UART_DM_CR_GENERAL_CMD(x) ((x) << 8) + +#define MSM_BOOT_UART_DM_GCMD_NULL MSM_BOOT_UART_DM_CR_GENERAL_CMD(0) +#define MSM_BOOT_UART_DM_GCMD_CR_PROT_EN MSM_BOOT_UART_DM_CR_GENERAL_CMD(1) +#define MSM_BOOT_UART_DM_GCMD_CR_PROT_DIS MSM_BOOT_UART_DM_CR_GENERAL_CMD(2) +#define MSM_BOOT_UART_DM_GCMD_RES_TX_RDY_INT MSM_BOOT_UART_DM_CR_GENERAL_CMD(3) +#define MSM_BOOT_UART_DM_GCMD_SW_FORCE_STALE MSM_BOOT_UART_DM_CR_GENERAL_CMD(4) +#define MSM_BOOT_UART_DM_GCMD_ENA_STALE_EVT MSM_BOOT_UART_DM_CR_GENERAL_CMD(5) +#define MSM_BOOT_UART_DM_GCMD_DIS_STALE_EVT MSM_BOOT_UART_DM_CR_GENERAL_CMD(6) + +/* UART Command Register */ +#if PERIPH_BLK_BLSP +#define MSM_BOOT_UART_DM_CR(base) ((base) + 0xA8) +#else +#define MSM_BOOT_UART_DM_CR(base) ((base) + 0x10) +#endif +#define MSM_BOOT_UART_DM_CR_RX_ENABLE (1 << 0) +#define MSM_BOOT_UART_DM_CR_RX_DISABLE (1 << 1) +#define MSM_BOOT_UART_DM_CR_TX_ENABLE (1 << 2) +#define MSM_BOOT_UART_DM_CR_TX_DISABLE (1 << 3) + +/* UART Status Register */ +#if PERIPH_BLK_BLSP +#define MSM_BOOT_UART_DM_SR(base) ((base) + 0x0A4) +#else +#define MSM_BOOT_UART_DM_SR(base) ((base) + 0x008) +#endif +#define MSM_BOOT_UART_DM_SR_RXRDY (1 << 0) +#define MSM_BOOT_UART_DM_SR_RXFULL (1 << 1) +#define MSM_BOOT_UART_DM_SR_TXRDY (1 << 2) +#define MSM_BOOT_UART_DM_SR_TXEMT (1 << 3) +#define MSM_BOOT_UART_DM_SR_UART_OVERRUN (1 << 4) +#define MSM_BOOT_UART_DM_SR_PAR_FRAME_ERR (1 << 5) +#define MSM_BOOT_UART_DM_RX_BREAK (1 << 6) +#define MSM_BOOT_UART_DM_HUNT_CHAR (1 << 7) +#define MSM_BOOT_UART_DM_RX_BRK_START_LAST (1 << 8) + +/* UART DM TX FIFO Registers - 4 */ +#if PERIPH_BLK_BLSP +#define MSM_BOOT_UART_DM_TF(base, x) ((base) + 0x100+(4*(x))) +#else +#define MSM_BOOT_UART_DM_TF(base, x) ((base) + 0x70+(4*(x))) +#endif + +/* UART Interrupt Status Register */ +#if PERIPH_BLK_BLSP +#define MSM_BOOT_UART_DM_ISR(base) ((base) + 0xB4) +#else +#define MSM_BOOT_UART_DM_ISR(base) ((base) + 0x14) +#endif + +#define MSM_BOOT_UART_DM_TXLEV (1 << 0) +#define MSM_BOOT_UART_DM_RXHUNT (1 << 1) +#define MSM_BOOT_UART_DM_RXBRK_CHNG (1 << 2) +#define MSM_BOOT_UART_DM_RXSTALE (1 << 3) +#define MSM_BOOT_UART_DM_RXLEV (1 << 4) +#define MSM_BOOT_UART_DM_DELTA_CTS (1 << 5) +#define MSM_BOOT_UART_DM_CURRENT_CTS (1 << 6) +#define MSM_BOOT_UART_DM_TX_READY (1 << 7) +#define MSM_BOOT_UART_DM_TX_ERROR (1 << 8) +#define MSM_BOOT_UART_DM_TX_DONE (1 << 9) +#define MSM_BOOT_UART_DM_RXBREAK_START (1 << 10) +#define MSM_BOOT_UART_DM_RXBREAK_END (1 << 11) +#define MSM_BOOT_UART_DM_PAR_FRAME_ERR_IRQ (1 << 12) + +#define NONE 0x0 + +#if UARTx_DM_BASE == NONE +void serial_putc(char c){ } /* quiet mode */ +#else + +void serial_putc(char c){ + unsigned int base = UARTx_DM_BASE; + unsigned int num_of_chars = 1; + unsigned int tx_word = 0; + + /* Write to NO_CHARS_FOR_TX register number of characters + * to be transmitted. However, before writing TX_FIFO must + * be empty as indicated by TX_READY interrupt in IMR register + * + * Check if transmit FIFO is empty. + * If not we'll wait for TX_READY interrupt. */ + if(!(readl(MSM_BOOT_UART_DM_SR(base)) & MSM_BOOT_UART_DM_SR_TXEMT)){ + while(!(readl(MSM_BOOT_UART_DM_ISR(base)) & MSM_BOOT_UART_DM_TX_READY)); + } + + /* We are here. FIFO is ready to be written. */ + /* Write number of characters to be written */ + writel(num_of_chars, MSM_BOOT_UART_DM_NO_CHARS_FOR_TX(base)); + + /* Clear TX_READY interrupt */ + writel(MSM_BOOT_UART_DM_GCMD_RES_TX_RDY_INT, MSM_BOOT_UART_DM_CR(base)); + + /* Wait till TX FIFO has space */ + while(!(readl(MSM_BOOT_UART_DM_SR(base)) & MSM_BOOT_UART_DM_SR_TXRDY)); + if(c == '\n'){ + /* Replace linefeed char "\n" with carriage return + linefeed "\r\n". */ + tx_word = '\r'; + tx_word <<= 8; + tx_word |= '\n'; + }else{ + tx_word = c; /* We need only one char */ + } + /* TX FIFO has space. Write the chars */ + writel(tx_word, MSM_BOOT_UART_DM_TF(base, 0)); +} + +#endif /* UARTx_DM_BASE == NONE */ + +/* +//for HEAD loader debug +#include "printf.h" +#include "types.h" +void owl_debug(u32 r0, u32 r1, u32 r2){ + printf("Hello OWL! 0x%x, 0x%x, 0x%x\n", r0, r1, r2); +} +int raise (int signum) +{ + return 0; +} +*/ diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/start.S b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/start.S new file mode 100644 index 0000000000..54ead220b0 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/start.S @@ -0,0 +1,69 @@ +.globl _start +_start: + mov r7, r1 @save REAL _start addr + mov r6, r2 @ARCH value + +/* set the cpu to SVC32 mode */ + mrs r0, cpsr + bic r0, r0, #0x1f + orr r0, r0, #0xd3 + msr cpsr,r0 + +/* Move vector table + Set vector address in CP15 VBAR register */ + ldr r0, =vector_table_stub + mcr p15, 0, r0, c12, c0, 0 @Set VBAR + +/* Lets clear a bss */ +clear_bss: + ldr r0, =_bss_start + ldr r1, =_bss_end + mov r2, #0x00000000 /* clear */ +clbss_l: + cmp r0, r1 /* clear loop... */ + bhs clbss_e /* if reached end of bss, exit */ + str r2, [r0] + add r0, r0, #4 + b clbss_l +clbss_e: + +/* Set stackpointer in internal RAM to call loader_main */ +call_loader_main: + /* stack grows left! <--SP--TEXT_BASE,_start--END of program */ + ldr sp, =(CONFIG_SYS_TEXT_BASE) + bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ + mov r0, r7 @REAL _start addr + mov r1, r6 @ARCH + bl loader_main + +.globl owl_get_sp +owl_get_sp: + mov r0, sp + mov pc, lr @ back to my caller + +/* stub for IRQ vector table. we do not need + interrupt handlers, exception handlers, etc... + so if one of them happens, then just + wait(in deadloop) for the watchdogs reboot! */ +.globl vector_table_stub +.align 4 +vector_table_stub: + b vector_table_stub @dead loop + ldr pc, _undefined_instruction + ldr pc, _software_interrupt + ldr pc, _prefetch_abort + ldr pc, _data_abort + ldr pc, _not_used + ldr pc, _irq + ldr pc, _fiq +_undefined_instruction: .word vector_table_stub +_software_interrupt: .word vector_table_stub +_prefetch_abort: .word vector_table_stub +_data_abort: .word vector_table_stub +_not_used: .word vector_table_stub +_irq: .word vector_table_stub +_fiq: .word vector_table_stub +_pad: .word 0x12345678 /* now 16*4=64 */ +.global _end_vector_table_stub +_end_vector_table_stub: + .balignl 16,0xdeadbeef diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/src/watchdog.c b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/watchdog.c new file mode 100644 index 0000000000..72b3b7e05d --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/src/watchdog.c @@ -0,0 +1,96 @@ +/* + * Auxiliary kernel loader for Qualcom IPQ-4XXX/806X based boards + * + * Copyright (C) 2019 Sergey Sergeev + * + * Some structures and code has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include + +#define WDT_RESETUP_PERIOD 10 + +#ifdef CONFIG_IPQ4XXX +#define WDT_BASE 0xB017000 +#define WDT_RST (WDT_BASE + 0x4) +#define WDT_EN (WDT_BASE + 0x8) +#define WDT_STS (WDT_BASE + 0xc) +#define WDT_BARK_TIME (WDT_BASE + 0x10) +#define WDT_BITE_TIME (WDT_BASE + 0x14) + +#define GCNT_PSHOLD 0x004AB000 + +void watchdog_setup(int period){ + writel(0, WDT_EN); + writel(1, WDT_RST); + writel(period * 65536, WDT_BARK_TIME); + writel(period * 65536, WDT_BITE_TIME); + writel(1, WDT_EN); +} + +void reset_cpu(unsigned long addr) +{ + /* clear ps-hold bit to reset the soc */ + writel(0, GCNT_PSHOLD); + while (1); +} + +#endif /* CONFIG_IPQ4XXX */ + +#ifdef CONFIG_IPQ806X +#define MSM_CLK_CTL_BASE 0x00900000 + +#define MSM_TMR_BASE 0x0200A000 +#define MSM_GPT_BASE (MSM_TMR_BASE + 0x04) +#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24) + +#define GPT_REG(off) (MSM_GPT_BASE + (off)) +#define DGT_REG(off) (MSM_DGT_BASE + (off)) + +#define APCS_WDT0_EN (MSM_TMR_BASE + 0x0040) +#define APCS_WDT0_RST (MSM_TMR_BASE + 0x0038) +#define APCS_WDT0_BARK_TIME (MSM_TMR_BASE + 0x004C) +#define APCS_WDT0_BITE_TIME (MSM_TMR_BASE + 0x005C) + +#define APCS_WDT0_CPU0_WDOG_EXPIRED_ENABLE (MSM_CLK_CTL_BASE + 0x3820) + +/* Watchdog bite time set to default reset value */ +#define RESET_WDT_BITE_TIME 0x31F3 + +/* Watchdog bark time value is ketp larger than the watchdog timeout + * of 0x31F3, effectively disabling the watchdog bark interrupt + */ +#define RESET_WDT_BARK_TIME (5 * RESET_WDT_BITE_TIME) + +void watchdog_setup(int period){ + writel(0, APCS_WDT0_EN); + writel(1, APCS_WDT0_RST); + writel(period * 32768, APCS_WDT0_BARK_TIME); + writel(period * 32768, APCS_WDT0_BITE_TIME); + writel(1, APCS_WDT0_EN); + writel(1, APCS_WDT0_CPU0_WDOG_EXPIRED_ENABLE); +} + +void reset_cpu(unsigned long addr) +{ + writel(0, APCS_WDT0_EN); + writel(1, APCS_WDT0_RST); + writel(RESET_WDT_BARK_TIME, APCS_WDT0_BARK_TIME); + writel(RESET_WDT_BITE_TIME, APCS_WDT0_BITE_TIME); + writel(1, APCS_WDT0_EN); + writel(1, APCS_WDT0_CPU0_WDOG_EXPIRED_ENABLE); + + for(;;); +} +#endif /* CONFIG_IPQ806X */ + +void watchdog_resetup(void){ + watchdog_setup(WDT_RESETUP_PERIOD); +} diff --git a/target/linux/ipq40xx/image/ipq-aux-loader/src/ubi/loader.ubi.ini b/target/linux/ipq40xx/image/ipq-aux-loader/src/ubi/loader.ubi.ini new file mode 100644 index 0000000000..96fd301cf8 --- /dev/null +++ b/target/linux/ipq40xx/image/ipq-aux-loader/src/ubi/loader.ubi.ini @@ -0,0 +1,9 @@ +[Kernel] +mode = ubi +image = bin/loader.ubifs +vol_name = Kernel +vol_size = 15000000 +;vol_flags = 0 +vol_type = dynamic +vol_alignment = 1 +vol_id = 0 -- 2.30.2