diff --git a/payloads/libpayload/Kconfig b/payloads/libpayload/Kconfig index f7d78e069d..382f5af751 100644 --- a/payloads/libpayload/Kconfig +++ b/payloads/libpayload/Kconfig @@ -121,6 +121,14 @@ config ARCH_ARM64 help Support the ARM64 architecture +config ARCH_MOCK + bool "Mock architecture (for unit tests)" + help + This enables the mock architecture (for unit tests) that is intended + to be used for testing purposes, to either test payloads or libpayload itself. + It provides necessary headers, but requires mocking (providing implementation + for) arch-specific functions. + endchoice config MULTIBOOT @@ -150,6 +158,7 @@ config BASE_ADDRESS default 0x04000000 if ARCH_ARM default 0x80100000 if ARCH_ARM64 default 0x00100000 if ARCH_X86 + default 0x00000000 if ARCH_MOCK help This is the base address for the payload. @@ -480,3 +489,4 @@ config IO_ADDRESS_SPACE source "arch/arm/Kconfig" source "arch/arm64/Kconfig" source "arch/x86/Kconfig" +source "arch/mock/Kconfig" diff --git a/payloads/libpayload/Makefile b/payloads/libpayload/Makefile index 90f6b23836..923106248c 100644 --- a/payloads/libpayload/Makefile +++ b/payloads/libpayload/Makefile @@ -74,6 +74,14 @@ HOSTCC = gcc HOSTCXX = g++ HOSTCFLAGS := -I$(srck) -I$(objk) -g HOSTCXXFLAGS := -I$(srck) -I$(objk) +HOSTAS ?= as +HOSTLD ?= ld +HOSTNM ?= nm +HOSTOBJCOPY ?= objcopy +HOSTOBJDUMP ?= objdump +HOSTREADELF ?= readelf +HOSTSTRIP ?= strip +HOSTAR ?= ar DOXYGEN := doxygen DOXYGEN_OUTPUT_DIR := doxygen @@ -95,6 +103,7 @@ include $(HAVE_DOTCONFIG) ARCHDIR-$(CONFIG_LP_ARCH_ARM) := arm ARCHDIR-$(CONFIG_LP_ARCH_ARM64) := arm64 ARCHDIR-$(CONFIG_LP_ARCH_X86) := x86 +ARCHDIR-$(CONFIG_LP_ARCH_MOCK) := mock ARCH-y := $(ARCHDIR-y) @@ -103,6 +112,7 @@ ARCH-y := $(ARCHDIR-y) ARCH-$(CONFIG_LP_ARCH_ARM) := arm ARCH-$(CONFIG_LP_ARCH_ARM64) := arm64 ARCH-$(CONFIG_LP_ARCH_X86) := x86_32 +ARCH-$(CONFIG_LP_ARCH_MOCK) := mock # Three cases where we don't need fully populated $(obj) lists: # 1. when no .config exists @@ -129,6 +139,31 @@ real-all: config else +ifeq ($(CONFIG_LP_ARCH_MOCK),y) + +# Create empty xcompile to satisfy install script +$(shell echo '' > $(xcompile)) + +CC := $(HOSTCC) +CC-mock := $(HOSTCC) +AS := $(HOSTAS) +AS-mock := $(HOSTAS) +LD := $(HOSTLD) +LD-mock := $(HOSTLD) +NM := $(HOSTNM) +NM-mock := $(HOSTNM) +OBJCOPY := $(HOSTOBJCOPY) +OBJCOPY-mock := $(HOSTOBJCOPY) +OBJDUMP := $(HOSTOBJDUMP) +OBJDUMP-mock := $(HOSTOBJDUMP) +READELF := $(HOSTREADELF) +READELF-mock := $(HOSTEADELF) +STRIP := $(HOSTSTRIP) +STRIP-mock := $(HOSTSTRIP) +AR := $(HOSTAR) +AR-mock := $(HOSTAR) +else + # in addition to the dependency below, create the file if it doesn't exist # to silence stupid warnings about a file that would be generated anyway. $(if $(wildcard $(xcompile)),,$(shell \ @@ -152,12 +187,16 @@ OBJDUMP := $(OBJDUMP_$(ARCH-y)) READELF := $(READELF_$(ARCH-y)) STRIP := $(STRIP_$(ARCH-y)) AR := $(AR_$(ARCH-y)) +endif CFLAGS += -std=gnu11 $(CFLAGS_$(ARCH-y)) ifneq ($(INNER_SCANBUILD),y) ifeq ($(CONFIG_LP_COMPILER_LLVM_CLANG),y) -CC:=clang -m32 +CC:=clang +ifneq ($(CONFIG_LP_ARCH_MOCK),y) +CC += -m32 +endif HOSTCC:=clang endif endif diff --git a/payloads/libpayload/Makefile.inc b/payloads/libpayload/Makefile.inc index a67ba77b67..df0f14298f 100644 --- a/payloads/libpayload/Makefile.inc +++ b/payloads/libpayload/Makefile.inc @@ -33,6 +33,7 @@ export KERNELVERSION := 0.2.0 ARCHDIR-$(CONFIG_LP_ARCH_ARM) := arm ARCHDIR-$(CONFIG_LP_ARCH_ARM64) := arm64 ARCHDIR-$(CONFIG_LP_ARCH_X86) := x86 +ARCHDIR-$(CONFIG_LP_ARCH_MOCK) := mock DESTDIR ?= install real-target: lib diff --git a/payloads/libpayload/arch/mock/Kconfig b/payloads/libpayload/arch/mock/Kconfig new file mode 100644 index 0000000000..3903a76005 --- /dev/null +++ b/payloads/libpayload/arch/mock/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +if ARCH_MOCK + +config ARCH_MOCK_BIG_ENDIAN + bool "Use big-endian for mock architecture" + default n + help + This option enables big-endinan support in the code. + +config ARCH_SPECIFIC_OPTIONS + def_bool y + select LITTLE_ENDIAN if !ARCH_MOCK_BIG_ENDIAN + select BIG_ENDIAN if ARCH_MOCK_BIG_ENDIAN + +endif diff --git a/payloads/libpayload/arch/mock/Makefile.inc b/payloads/libpayload/arch/mock/Makefile.inc new file mode 100644 index 0000000000..f15f0f9f1d --- /dev/null +++ b/payloads/libpayload/arch/mock/Makefile.inc @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only + +head.o-y += head.c + +libc-y += virtual.c + +libcbfs-$(CONFIG_LP_CBFS) += mock_media.c diff --git a/payloads/libpayload/arch/mock/head.c b/payloads/libpayload/arch/mock/head.c new file mode 100644 index 0000000000..55a691f910 --- /dev/null +++ b/payloads/libpayload/arch/mock/head.c @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* This file is empty on purpose. It should not be used. */ diff --git a/payloads/libpayload/arch/mock/libpayload.ldscript b/payloads/libpayload/arch/mock/libpayload.ldscript new file mode 100644 index 0000000000..6842c9ad5c --- /dev/null +++ b/payloads/libpayload/arch/mock/libpayload.ldscript @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* This file is empty on purpose. It is present only to satisfy install script */ diff --git a/payloads/libpayload/arch/mock/mock_media.c b/payloads/libpayload/arch/mock/mock_media.c new file mode 100644 index 0000000000..2bb06edc29 --- /dev/null +++ b/payloads/libpayload/arch/mock/mock_media.c @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +int libpayload_init_default_cbfs_media(struct cbfs_media *media); + +__attribute__((weak)) int libpayload_init_default_cbfs_media(struct cbfs_media *media) +{ + return -1; +} diff --git a/payloads/libpayload/arch/mock/virtual.c b/payloads/libpayload/arch/mock/virtual.c new file mode 100644 index 0000000000..6f369d2dcb --- /dev/null +++ b/payloads/libpayload/arch/mock/virtual.c @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +unsigned long virtual_offset = 0; + +int getpagesize(void) +{ + return 4096; +} diff --git a/payloads/libpayload/bin/lpgcc b/payloads/libpayload/bin/lpgcc index c0fe56adf5..3a76f13d17 100755 --- a/payloads/libpayload/bin/lpgcc +++ b/payloads/libpayload/bin/lpgcc @@ -91,13 +91,24 @@ if [ "$CONFIG_LP_ARCH_X86" = "y" ]; then _ARCHEXTRA="-m32 " _ARCH=x86 fi +if [ "$CONFIG_LP_ARCH_MOCK" = "y" ]; then + _ARCHINCDIR=$_INCDIR/mock + _ARCHLIBDIR=$_LIBDIR/mock + _ARCHEXTRA="" + _ARCH=mock +fi if [ -f $_LIBDIR/libpayload.ldscript ]; then _LDDIR=$_LIBDIR elif [ -f $BASE/../arch/$_ARCH/libpayload.ldscript ]; then _LDDIR=$BASE/../arch/$_ARCH fi -_LDSCRIPT="-Wl,-T,$_LDDIR/libpayload.ldscript" +# Host arch should youse default linker script +if [ "$CONFIG_LP_ARCH_MOCK" = "y" ]; then + _LDSCRIPT="" +else + _LDSCRIPT="-Wl,-T,$_LDDIR/libpayload.ldscript" +fi trygccoption() { $DEFAULT_CC $1 -S -xc /dev/null -o /dev/null &> /dev/null diff --git a/payloads/libpayload/include/mock/arch/barrier.h b/payloads/libpayload/include/mock/arch/barrier.h new file mode 100644 index 0000000000..aed115bb64 --- /dev/null +++ b/payloads/libpayload/include/mock/arch/barrier.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __ARCH_BARRIER_H__ +#define __ARCH_BARRIER_H__ + +/* No memory barrier on mock build */ +#define mb() +/* No read memory barrier on mock build */ +#define rmb() +/* No write memory barrier on mock build */ +#define wmb() + +#endif /* __ARCH_BARRIER_H__ */ diff --git a/payloads/libpayload/include/mock/arch/cache.h b/payloads/libpayload/include/mock/arch/cache.h new file mode 100644 index 0000000000..1e71d5e0e2 --- /dev/null +++ b/payloads/libpayload/include/mock/arch/cache.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __ARCH_CACHE_H__ +#define __ARCH_CACHE_H__ + +/* No support for cache in the mock architecture */ + +#define dmb() +#define dsb() +#define dcache_clean_all() +#define dcache_clean_by_mva(addr, len) +#define dcache_invalidate_all() +#define dcache_invalidate_by_mva(addr, len) +#define dcache_clean_invalidate_all() +#define dcache_clean_invalidate_by_mva(addr, len) +#define cache_sync_instructions() + +#endif /* __ARCH_CACHE_H__ */ diff --git a/payloads/libpayload/include/mock/arch/io.h b/payloads/libpayload/include/mock/arch/io.h new file mode 100644 index 0000000000..2bb625562e --- /dev/null +++ b/payloads/libpayload/include/mock/arch/io.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ARCH_IO_H +#define _ARCH_IO_H + +#include + +/* Functions in this file are unimplemented by default. Tests are expected to implement + mocks for these functions, if tests will call functions using functions listed below. */ + +uint8_t readb(volatile const void *_a); +uint16_t readw(volatile const void *_a); +uint32_t readl(volatile const void *_a); + +void writeb(uint8_t _v, volatile void *_a); +void writew(uint16_t _v, volatile void *_a); +void writel(uint32_t _v, volatile void *_a); + +uint8_t read8(volatile const void *addr); +uint16_t read16(volatile const void *addr); +uint32_t read32(volatile const void *addr); +uint64_t read64(volatile const void *addr); + +void write8(volatile void *addr, uint8_t val); +void write16(volatile void *addr, uint16_t val); +void write32(volatile void *addr, uint32_t val); +void write64(volatile void *addr, uint64_t val); + +#endif /* _ARCH_IO_H */ diff --git a/payloads/libpayload/include/mock/arch/types.h b/payloads/libpayload/include/mock/arch/types.h new file mode 100644 index 0000000000..8f090caa9b --- /dev/null +++ b/payloads/libpayload/include/mock/arch/types.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ARCH_TYPES_H +#define _ARCH_TYPES_H + +typedef unsigned char uint8_t; +typedef unsigned char u8; +typedef signed char int8_t; +typedef signed char s8; + +typedef unsigned short uint16_t; +typedef unsigned short u16; +typedef signed short int16_t; +typedef signed short s16; + +typedef unsigned int uint32_t; +typedef unsigned int u32; +typedef signed int int32_t; +typedef signed int s32; + +typedef unsigned long long uint64_t; +typedef unsigned long long u64; +typedef signed long long int64_t; +typedef signed long long s64; + +typedef long time_t; +typedef long suseconds_t; + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#endif /* _ARCH_TYPES_H */ diff --git a/payloads/libpayload/include/mock/arch/virtual.h b/payloads/libpayload/include/mock/arch/virtual.h new file mode 100644 index 0000000000..bf786d5256 --- /dev/null +++ b/payloads/libpayload/include/mock/arch/virtual.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ARCH_VIRTUAL_H +#define _ARCH_VIRTUAL_H + +/* virtual_offset has to be declared if used */ +extern unsigned long virtual_offset; + +#define virt_to_phys(virt) ((unsigned long)(virt) + virtual_offset) +#define phys_to_virt(phys) ((void *)((unsigned long)(phys) - virtual_offset)) + +#define virt_to_bus(addr) virt_to_phys(addr) +#define bus_to_virt(addr) phys_to_virt(addr) + +#endif diff --git a/payloads/libpayload/sample/Makefile b/payloads/libpayload/sample/Makefile index 637e45dee1..1249e9a017 100644 --- a/payloads/libpayload/sample/Makefile +++ b/payloads/libpayload/sample/Makefile @@ -26,8 +26,13 @@ ## SUCH DAMAGE. ## + # Sample libpayload Makefile. include ../.config +ifeq ($(CONFIG_LP_ARCH_MOCK),y) +$(error This sample program does not support ARCH_MOCK. Use sample/arch_mock instead) +endif + include ../build/xcompile ARCH-$(CONFIG_LP_ARCH_ARM) := arm diff --git a/payloads/libpayload/sample/arch_mock/Makefile b/payloads/libpayload/sample/arch_mock/Makefile new file mode 100644 index 0000000000..a1e748111e --- /dev/null +++ b/payloads/libpayload/sample/arch_mock/Makefile @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# Sample libpayload Makefile for ARCH_MOCK +# ARCH_MOCK is not intended to be used with xcompile +include ../../.config + +ifneq ($(CONFIG_LP_ARCH_MOCK),y) +$(error This example supports ARCH_MOCK only.) +endif + +CC := gcc +AS := as +OBJCOPY := objcopy +LIBPAYLOAD_DIR := ../../install/libpayload +CFLAGS := -fno-builtin -Wall -Werror -Os \ + -include $(LIBPAYLOAD_DIR)/include/kconfig.h \ + -include $(LIBPAYLOAD_DIR)/include/compiler.h \ + -I $(LIBPAYLOAD_DIR)/include \ + -I $(LIBPAYLOAD_DIR)/include/mock \ + -ffunction-sections \ + -fdata-sections -g3 +LDFLAGS := -Wl,--gc-sections +TARGET := hello +OBJS := $(TARGET).o +OBJS-mock := $(TARGET)_mocks.o +LIBPAYLOAD-local := libpayload.a +mocks := console_write + +all: $(TARGET).elf + +$(TARGET).elf: $(OBJS) $(OBJS-mock) $(LIBPAYLOAD-local) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBPAYLOAD-local) \ + -Wl,--exclude-libs,ALL -lc $(OBJS-mock) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.S.o: %.S + $(AS) --32 -o $@ $< + +# Copy libpayload and weaken all mocked symbols +$(LIBPAYLOAD-local): $(LIBPAYLOAD_DIR)/lib/libpayload.a + $(OBJCOPY) $(foreach mock,$(mocks),--weaken-symbol=$(mock)) $< $@ + +clean: + rm -f $(TARGET).elf *.o $(LIBPAYLOAD-local) + +distclean: clean diff --git a/payloads/libpayload/sample/arch_mock/hello.c b/payloads/libpayload/sample/arch_mock/hello.c new file mode 100644 index 0000000000..5a96e42e75 --- /dev/null +++ b/payloads/libpayload/sample/arch_mock/hello.c @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* Example file for libpayload. */ + +#include +#include + +int main(void) +{ + printf("Hello world!\n"); + halt(); + return 0; +} diff --git a/payloads/libpayload/sample/arch_mock/hello_mocks.c b/payloads/libpayload/sample/arch_mock/hello_mocks.c new file mode 100644 index 0000000000..84e86ff257 --- /dev/null +++ b/payloads/libpayload/sample/arch_mock/hello_mocks.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include + +/* Use libc version. calling exit() or abort() would cause infinite recursion */ +__attribute__((noreturn)) +void _exit(int); + +__attribute__((noreturn)) +void halt(void) +{ + _exit(0); +} + +#define TEST_SYMBOL(symbol, value) asm(".set " #symbol ", " #value "\n\t.globl " #symbol) + +#define TEST_REGION(region, size) uint8_t _##region[size]; \ + TEST_SYMBOL(_e##region, _##region + size); \ + TEST_SYMBOL(_##region##_size, size) + +TEST_REGION(heap, CONFIG_LP_HEAP_SIZE); + +uint64_t timer_raw_value(void) +{ + return 0; +} + +uint64_t timer_hz(void) +{ + return 0; +} + +/* Not present in libpayload. Can be used to write to real stdout. */ +ssize_t write(int fildes, const void *buf, size_t nbyte); + +void console_write(const void *buffer, size_t count) +{ + write(1, buffer, count); +}