| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| /* This file is empty on purpose. It should not be used. */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| #include <cbfs.h> | ||
|
|
||
| int libpayload_init_default_cbfs_media(struct cbfs_media *media); | ||
|
|
||
| __attribute__((weak)) int libpayload_init_default_cbfs_media(struct cbfs_media *media) | ||
| { | ||
| return -1; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| #include <unistd.h> | ||
|
|
||
| unsigned long virtual_offset = 0; | ||
|
|
||
| int getpagesize(void) | ||
| { | ||
| return 4096; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| CONFIG_LP_ARCH_MOCK=y | ||
| CONFIG_LP_ARCH_BIG_ENDIAN=n |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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__ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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__ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| #ifndef _ARCH_IO_H | ||
| #define _ARCH_IO_H | ||
|
|
||
| #include <inttypes.h> | ||
|
|
||
| /* 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 */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| /* Example file for libpayload. */ | ||
|
|
||
| #include <libpayload-config.h> | ||
| #include <libpayload.h> | ||
|
|
||
| int main(void) | ||
| { | ||
| printf("Hello world!\n"); | ||
| halt(); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| #include <libpayload-config.h> | ||
| #include <arch/types.h> | ||
| #include <stddef.h> | ||
| #include <unistd.h> | ||
| #include <stdio.h> | ||
|
|
||
| /* 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); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,277 @@ | ||
| # SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| testsrc := $(top)/tests | ||
|
|
||
| # Place the build output in one of two places depending on COV, so that code | ||
| # built with code coverage never mixes with code built without code coverage. | ||
| ifeq ($(COV),1) | ||
| testobj := $(obj)/coverage | ||
| else | ||
| testobj := $(obj)/tests | ||
| endif | ||
| coverage-dir := $(testobj)/coverage_reports | ||
|
|
||
| coreboottop := ../../ | ||
|
|
||
| cmockasrc := $(coreboottop)/3rdparty/cmocka | ||
| cmockaobj := $(objutil)/cmocka | ||
| CMOCKA_LIB := $(cmockaobj)/src/libcmocka.so | ||
|
|
||
| CMAKE := cmake | ||
|
|
||
| TEST_DEFAULT_CONFIG := $(top)/configs/config.unit-tests | ||
| TEST_DOTCONFIG := $(testobj)/.config | ||
| TEST_KCONFIG_AUTOHEADER := $(testobj)/libpayload-config.src.h | ||
| TEST_KCONFIG_AUTOCONFIG := $(testobj)/auto.conf | ||
| TEST_KCONFIG_DEPENDENCIES := $(testobj)/auto.conf.cmd | ||
| TEST_KCONFIG_SPLITCONFIG := $(testobj)/config/ | ||
| TEST_KCONFIG_TRISTATE := $(testobj)/tristate.conf | ||
| TEST_KCONFIG_NEGATIVES := 1 | ||
| TEST_KBUILD_KCONFIG := $(top)/Kconfig | ||
| TEST_CONFIG_ := CONFIG_LP_ | ||
|
|
||
|
|
||
| # Default includes | ||
| TEST_CFLAGS := -include include/kconfig.h -include include/compiler.h | ||
| TEST_CFLAGS += -Iinclude -Iinclude/mock | ||
| TEST_CFLAGS += -I$(dir $(TEST_KCONFIG_AUTOHEADER)) | ||
|
|
||
| # Test specific includes | ||
| TEST_CFLAGS += -I$(testsrc)/include -I$(testsrc)/include/mocks | ||
| TEST_CFLAGS += -I$(cmockasrc)/include | ||
|
|
||
| # Minimal subset of warnings and errors. Tests can be less strict than actual build. | ||
| TEST_CFLAGS += -Wall -Wundef -Wstrict-prototypes -Wvla | ||
| TEST_CFLAGS += -Wwrite-strings -Wno-trigraphs -Wimplicit-fallthrough | ||
| TEST_CFLAGS += -Wstrict-aliasing -Wshadow -Werror | ||
|
|
||
| TEST_CFLAGS += -std=gnu11 -Os -ffunction-sections -fdata-sections -fno-builtin | ||
|
|
||
| # Make unit-tests detectable by the code | ||
| TEST_CFLAGS += -D__TEST__ | ||
|
|
||
| # Link against CMocka | ||
| TEST_LDFLAGS := -L$(dir $(CMOCKA_LIB)) -lcmocka -Wl,-rpath=$(dir $(CMOCKA_LIB)) | ||
|
|
||
| TEST_LDFLAGS += -Wl,--gc-sections | ||
|
|
||
| # Disable userspace relocations | ||
| TEST_CFLAGS += -fno-pie -fno-pic | ||
| TEST_LDFLAGS += -no-pie | ||
|
|
||
| ifeq ($(COV),1) | ||
| TEST_CFLAGS += --coverage | ||
| TEST_LDFLAGS += --coverage | ||
| endif | ||
|
|
||
|
|
||
| # Extra attributes for unit tests. Declated per each test. Only `srcs` is required. | ||
| attributes := cflags config mocks srcs | ||
|
|
||
| alltests := | ||
| subdirs := tests/crypto tests/curses tests/drivers tests/gdb tests/libc tests/libcbfs | ||
| subdirs += tests/liblz4 tests/liblzma tests/libpci | ||
|
|
||
| define tests-handler | ||
| alltests += $(1)$(2) | ||
| $(foreach attribute,$(attributes), \ | ||
| $(eval $(1)$(2)-$(attribute) += $($(2)-$(attribute)))) | ||
| $(foreach attribute,$(attributes), \ | ||
| $(eval $(2)-$(attribute) := )) | ||
| endef | ||
|
|
||
| # Copy attributes of one test to another | ||
| # $1 - input test name | ||
| # $2 - output test name | ||
| copy-test = $(foreach attribute,$(attributes), \ | ||
| $(eval $(strip $(2))-$(attribute) := $($(strip $(1))-$(attribute)))) | ||
|
|
||
| $(call add-special-class,tests) | ||
| $(call evaluate_subdirs) | ||
|
|
||
| # Create actual targets for unit test binaries | ||
| # $1 - test name | ||
| define TEST_CC_template | ||
|
|
||
| # Generate custom config.h redefining given config symbols, and declaring mocked | ||
| # functions weak. It is important that the compiler already sees that they are | ||
| # weak (and they aren't just turned weak at a later stage) to prevent certain | ||
| # optimizations that would break if the function gets replaced. (For clang this | ||
| # file needs to be marked `system_header` to prevent it from warning about | ||
| # `#pragma weak` entries without a matching function declaration, since there is | ||
| # no -Wno-xxx commandline for that.) | ||
| $(1)-config-file := $(testobj)/$(1)/libpayload-config.h | ||
| $$($(1)-config-file): $(TEST_KCONFIG_AUTOHEADER) | ||
| mkdir -p $$(dir $$@); | ||
| printf '// File generated by tests/Makefile.inc\n// Do not change\n' > $$@; | ||
| printf '#ifndef TEST_LIBPAYLOAD_CONFIG_H_\n' >> $$@; | ||
| printf '#define TEST_LIBPAYLOAD_CONFIG_H_\n' >> $$@; | ||
| printf '#include <%s>\n\n' "$(notdir $(TEST_KCONFIG_AUTOHEADER))" >> $$@; | ||
| for kv in $$($(1)-config); do \ | ||
| key="`echo $$$$kv | cut -d '=' -f -1`"; \ | ||
| value="`echo $$$$kv | cut -d '=' -f 2-`"; \ | ||
| printf '#undef %s\n' "$$$$key" >> $$@; \ | ||
| printf '#define %s %s\n\n' "$$$$key" "$$$$value" >> $$@; \ | ||
| done | ||
| printf '#ifdef __clang__\n' >> $$@; | ||
| printf '#pragma clang system_header\n' >> $$@; | ||
| printf '#endif\n\n' >> $$@; | ||
| printf '#ifdef __TEST_SRCOBJ__\n' >> $$@; | ||
| for m in $$($(1)-mocks); do \ | ||
| printf '#pragma weak %s\n' "$$$$m" >> $$@; \ | ||
| done | ||
| printf '#endif\n\n' >> $$@; | ||
| printf '#endif\n' >> $$@; | ||
|
|
||
| $($(1)-objs): TEST_CFLAGS += -I$$(dir $$($(1)-config-file)) \ | ||
| -D__TEST_NAME__=\"$(subst /,_,$(1))\" | ||
|
|
||
| # Give us a way to distinguish between libpayload source files and test files in the code. | ||
| $($(1)-srcobjs): TEST_CFLAGS += -D__TEST_SRCOBJ__ | ||
|
|
||
| # Compile sources and apply mocking/wrapping for selected symbols. | ||
| # For each listed mock add new symbol with prefix `__real_`, | ||
| # pointing to the same section:address. This will keep original | ||
| # function accessible if required. | ||
| $($(1)-objs): $(testobj)/$(1)/%.o: $$$$*.c $$($(1)-config-file) | ||
| mkdir -p $$(dir $$@) | ||
| $(HOSTCC) $$(TEST_CFLAGS) $($(1)-cflags) -MMD \ | ||
| -MF $$(basename $$@).d -MT $$@ -c $$< -o $$@.orig | ||
| objcopy_wrap_flags=''; \ | ||
| for sym in $$($(1)-mocks); do \ | ||
| sym_line="$$$$($(HOSTOBJDUMP) -t $$@.orig \ | ||
| | grep -E \"[0-9a-fA-F]+\\s+w\\s+F\\s+.*\\s$$$$sym$$$$\")"; \ | ||
| if [ ! -z "$$$$sym_line" ] ; then \ | ||
| addr="$$$$(echo \"$$$$sym_line\" | awk '{ print $$$$1 }')"; \ | ||
| section="$$$$(echo \"$$$$sym_line\" | awk '{ print $$$$(NF - 2) }')"; \ | ||
| objcopy_wrap_flags="$$$$objcopy_wrap_flags --add-symbol __real_$$$${sym}=$$$${section}:0x$$$${addr},function,global"; \ | ||
| fi \ | ||
| done ; \ | ||
| $(HOSTOBJCOPY) $$@.orig $$$$objcopy_wrap_flags $$@ | ||
|
|
||
| $($(1)-bin): $($(1)-objs) $(CMOCKA_LIB) | ||
| $(HOSTCC) $$^ $($(1)-cflags) $$(TEST_LDFLAGS) -o $$@ | ||
|
|
||
| endef | ||
|
|
||
| $(foreach test,$(alltests), \ | ||
| $(eval $(test)-srcobjs := $(addprefix $(testobj)/$(test)/, \ | ||
| $(patsubst %.c,%.o,$(filter-out tests/%,$($(test)-srcs))))) \ | ||
| $(eval $(test)-objs := $(addprefix $(testobj)/$(test)/, \ | ||
| $(patsubst %.c,%.o,$($(test)-srcs)))) \ | ||
| $(eval $(test)-bin := $(testobj)/$(test)/run)) | ||
| $(foreach test,$(alltests), \ | ||
| $(eval $(call TEST_CC_template,$(test)))) | ||
| $(foreach test,$(alltests), \ | ||
| $(eval all-test-objs += $($(test)-objs)) \ | ||
| $(eval test-bins += $($(test)-bin))) | ||
|
|
||
| DEPENDENCIES += $(addsuffix .d,$(basename $(all-test-objs))) | ||
| -include $(DEPENDENCIES) | ||
|
|
||
| # Build CMocka | ||
| $(CMOCKA_LIB): | ||
| echo "*** Building CMOCKA ***" | ||
| mkdir -p $(cmockaobj) | ||
| cd $(cmockaobj) && $(CMAKE) $(abspath $(cmockasrc)) | ||
| $(MAKE) -C $(cmockaobj) | ||
|
|
||
| # Kconfig targets | ||
| $(TEST_DOTCONFIG): | ||
| mkdir -p $(dir $@) | ||
| cp $(TEST_DEFAULT_CONFIG) $(TEST_DOTCONFIG) | ||
|
|
||
| $(TEST_KCONFIG_AUTOHEADER): TEST_KCONFIG_FLAGS := DOTCONFIG=$(TEST_DOTCONFIG) \ | ||
| KCONFIG_AUTOHEADER=$(TEST_KCONFIG_AUTOHEADER) \ | ||
| KCONFIG_AUTOCONFIG=$(TEST_KCONFIG_AUTOCONFIG) \ | ||
| KCONFIG_DEPENDENCIES=$(TEST_KCONFIG_DEPENDENCIES) \ | ||
| KCONFIG_SPLITCONFIG=$(TEST_KCONFIG_SPLITCONFIG) \ | ||
| KCONFIG_TRISTATE=$(TEST_KCONFIG_TRISTATE) \ | ||
| KCONFIG_NEGATIVES=$(TEST_KCONFIG_NEGATIVES) \ | ||
| KBUILD_KCONFIG=$(TEST_KBUILD_KCONFIG) \ | ||
| KBUILD_DEFCONFIG=$(TEST_DEFAULT_CONFIG) \ | ||
| CONFIG_=$(TEST_CONFIG_) | ||
|
|
||
| $(TEST_KCONFIG_AUTOHEADER): $(TEST_DOTCONFIG) $(objk)/conf | ||
| mkdir -p $(dir $@) | ||
| $(MAKE) $(TEST_KCONFIG_FLAGS) olddefconfig V=$(V) | ||
| $(MAKE) $(TEST_KCONFIG_FLAGS) syncconfig V=$(V) | ||
|
|
||
| $(TEST_KCONFIG_AUTOCONFIG): $(TEST_KCONFIG_AUTOHEADER) | ||
| true | ||
|
|
||
| .PHONY: $(alltests) $(addprefix clean-,$(alltests)) | ||
| .PHONY: unit-tests build-unit-tests run-unit-tests clean-unit-tests | ||
|
|
||
| ifeq ($(JUNIT_OUTPUT),y) | ||
| $(alltests): export CMOCKA_MESSAGE_OUTPUT=xml | ||
| $(alltests): export CMOCKA_XML_FILE=$(testobj)/junit-%g.xml | ||
| endif | ||
|
|
||
| $(alltests): $$($$(@)-bin) | ||
| rm -f $(testobj)/junit-libpayload-$(subst /,_,$(patsubst $(testobj)/%/,%,$(dir $^)))\(*\).xml | ||
| rm -f $(testobj)/$(subst /,_,$^).failed | ||
| -$^ || echo failed > $(testobj)/$(subst /,_,$^).failed | ||
|
|
||
| # Build a code coverage report by collecting all the gcov files into a single | ||
| # report. If COV is not set, this might be a user error, and they're trying | ||
| # to generate a coverage report without first having built and run the code | ||
| # with code coverage. absence of COV=1 will be corrected. | ||
|
|
||
| .PHONY: coverage-report clean-coverage-report | ||
|
|
||
| ifeq ($(COV),1) | ||
| coverage-report: | ||
| lcov -o $(testobj)/tests.info -c -d $(testobj) --exclude '$(testsrc)/*' | ||
| genhtml -q -o $(coverage-dir) -t "coreboot unit tests" -s $(testobj)/tests.info | ||
|
|
||
| clean-coverage-report: | ||
| rm -Rf $(coverage-dir) | ||
| else | ||
| coverage-report: | ||
| COV=1 V=$(V) $(MAKE) coverage-report | ||
|
|
||
| clean-coverage-report: | ||
| COV=1 V=$(V) $(MAKE) clean-coverage-report | ||
| endif | ||
|
|
||
| unit-tests: build-unit-tests run-unit-tests | ||
|
|
||
| build-unit-tests: $(test-bins) | ||
|
|
||
| run-unit-tests: $(alltests) | ||
| if [ `find $(testobj) -name '*.failed' | wc -l` -gt 0 ]; then \ | ||
| echo "**********************"; \ | ||
| echo " TESTS FAILED"; \ | ||
| echo "**********************"; \ | ||
| exit 1; \ | ||
| else \ | ||
| echo "**********************"; \ | ||
| echo " ALL TESTS PASSED"; \ | ||
| echo "**********************"; \ | ||
| exit 0; \ | ||
| fi | ||
|
|
||
| $(addprefix clean-,$(alltests)): clean-% | ||
| rm -rf $(testobj)/$* | ||
|
|
||
| clean-unit-tests: | ||
| rm -rf $(testobj) | ||
|
|
||
| list-unit-tests: | ||
| @echo "unit-tests:" | ||
| for t in $(sort $(alltests)); do \ | ||
| echo " $$t"; \ | ||
| done | ||
|
|
||
| help-unit-tests help:: | ||
| @echo '*** libpayload unit-tests targets ***' | ||
| @echo ' Use "COV=1 make [target]" to enable code coverage for unit tests' | ||
| @echo ' unit-tests - Run all unit-tests from tests/' | ||
| @echo ' clean-unit-tests - Remove unit-tests build artifacts' | ||
| @echo ' list-unit-tests - List all unit-tests' | ||
| @echo ' <unit-test> - Build and run single unit-test' | ||
| @echo ' clean-<unit-test> - Remove single unit-test build artifacts' | ||
| @echo ' coverage-report - Generate a code coverage report' | ||
| @echo ' clean-coverage-report - Remove the code coverage report' | ||
| @echo |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| tests-y += speaker-test | ||
|
|
||
| speaker-test-srcs += tests/drivers/speaker-test.c | ||
| speaker-test-mocks += inb | ||
| speaker-test-mocks += outb | ||
| speaker-test-mocks += arch_ndelay | ||
| speaker-test-cflags += -include $(testsrc)/include/mocks/x86_io.h |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| #include <libpayload.h> | ||
| #include <mocks/x86_io.h> | ||
|
|
||
| /* Include source to gain access to private defines */ | ||
| #include "../drivers/speaker.c" | ||
|
|
||
| #include <tests/test.h> | ||
|
|
||
| void outb(unsigned char val, int port) | ||
| { | ||
| check_expected(val); | ||
| check_expected(port); | ||
| } | ||
|
|
||
| unsigned char inb(int port) | ||
| { | ||
| check_expected(port); | ||
| return mock_type(unsigned char); | ||
| } | ||
|
|
||
| static void setup_speaker_enable_calls(u16 freq, u8 port_val) | ||
| { | ||
| /* Minimal correct value should be at leaset 256. For values lowe than that, | ||
| counter will have an incorrect value. Regardless, there is */ | ||
| u16 reg16 = 1193180 / freq; | ||
|
|
||
| /* Select counter 2 */ | ||
| expect_value(outb, val, 0xb6); | ||
| expect_value(outb, port, I82C54_CONTROL_WORD_REGISTER); | ||
|
|
||
| /* Write freq. [LSB, MSB] */ | ||
| expect_value(outb, val, (u8)(reg16 & 0xff)); | ||
| expect_value(outb, port, I82C54_COUNTER2); | ||
| expect_value(outb, val, (u8)(reg16 >> 8)); | ||
| expect_value(outb, port, I82C54_COUNTER2); | ||
|
|
||
| /* Enable PC speaker */ | ||
| expect_value(inb, port, PC_SPEAKER_PORT); | ||
| will_return(inb, port_val); | ||
| expect_value(outb, val, port_val | 0x3); | ||
| expect_value(outb, port, PC_SPEAKER_PORT); | ||
| } | ||
|
|
||
| static void test_speaker_enable(void **state) | ||
| { | ||
| setup_speaker_enable_calls(1, 0); | ||
| speaker_enable(1); | ||
|
|
||
| setup_speaker_enable_calls(1, 0xff); | ||
| speaker_enable(1); | ||
|
|
||
| setup_speaker_enable_calls(1, 123); | ||
| speaker_enable(1); | ||
|
|
||
| setup_speaker_enable_calls(1, -1); | ||
| speaker_enable(1); | ||
|
|
||
| setup_speaker_enable_calls(-1, 0); | ||
| speaker_enable(-1); | ||
|
|
||
| setup_speaker_enable_calls(-1, 0xff); | ||
| speaker_enable(-1); | ||
|
|
||
| setup_speaker_enable_calls(-1, 222); | ||
| speaker_enable(-1); | ||
|
|
||
| setup_speaker_enable_calls(-1, -1); | ||
| speaker_enable(-1); | ||
|
|
||
| setup_speaker_enable_calls(10000, 0); | ||
| speaker_enable(10000); | ||
|
|
||
| setup_speaker_enable_calls(10000, 0xff); | ||
| speaker_enable(10000); | ||
|
|
||
| setup_speaker_enable_calls(10000, 91); | ||
| speaker_enable(10000); | ||
|
|
||
| setup_speaker_enable_calls(10000, -1); | ||
| speaker_enable(10000); | ||
| } | ||
|
|
||
| static void setup_speaker_disable_calls(u8 value) | ||
| { | ||
| expect_value(inb, port, PC_SPEAKER_PORT); | ||
| will_return(inb, value); | ||
| expect_value(outb, val, value & 0xfc); | ||
| expect_value(outb, port, PC_SPEAKER_PORT); | ||
| } | ||
|
|
||
| static void test_speaker_disable(void **state) | ||
| { | ||
| setup_speaker_disable_calls(0); | ||
| speaker_disable(); | ||
|
|
||
| setup_speaker_disable_calls(0xfc); | ||
| speaker_disable(); | ||
|
|
||
| setup_speaker_disable_calls(0xff); | ||
| speaker_disable(); | ||
|
|
||
| setup_speaker_disable_calls(0xff - 0xfc); | ||
| speaker_disable(); | ||
| } | ||
|
|
||
| void arch_ndelay(uint64_t ns) | ||
| { | ||
| check_expected(ns); | ||
| } | ||
|
|
||
| static void setup_speaker_tone_calls(u16 freq, unsigned int duration) | ||
| { | ||
| setup_speaker_enable_calls(freq, ~freq & 0xff); | ||
| expect_value(arch_ndelay, ns, (uint64_t)duration * NSECS_PER_MSEC); | ||
| setup_speaker_disable_calls(0xff); | ||
| expect_any(arch_ndelay, ns); | ||
| } | ||
|
|
||
| static void test_speaker_tone(void **state) | ||
| { | ||
| setup_speaker_tone_calls(500, 100); | ||
| speaker_tone(500, 100); | ||
|
|
||
| setup_speaker_tone_calls(4321, 0); | ||
| speaker_tone(4321, 0); | ||
|
|
||
| setup_speaker_tone_calls(-1, -1); | ||
| speaker_tone(-1, -1); | ||
|
|
||
| setup_speaker_tone_calls(10000, 1000); | ||
| speaker_tone(10000, 1000); | ||
|
|
||
| setup_speaker_tone_calls(433, 890); | ||
| speaker_tone(433, 890); | ||
| } | ||
|
|
||
| int main(void) | ||
| { | ||
| const struct CMUnitTest tests[] = { | ||
| cmocka_unit_test(test_speaker_enable), | ||
| cmocka_unit_test(test_speaker_disable), | ||
| cmocka_unit_test(test_speaker_tone), | ||
| }; | ||
|
|
||
| return lp_run_group_tests(tests, NULL, NULL); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| #ifndef TESTS_MOCKS_X86_IO_H_ | ||
| #define TESTS_MOCKS_X86_IO_H_ | ||
|
|
||
| unsigned int inl(int port); | ||
|
|
||
| unsigned short inw(int port); | ||
|
|
||
| unsigned char inb(int port); | ||
|
|
||
| void outl(unsigned int val, int port); | ||
|
|
||
| void outw(unsigned short val, int port); | ||
|
|
||
| void outb(unsigned char val, int port); | ||
|
|
||
| void outsl(int port, const void *addr, unsigned long count); | ||
|
|
||
| void outsw(int port, const void *addr, unsigned long count); | ||
|
|
||
| void outsb(int port, const void *addr, unsigned long count); | ||
|
|
||
| void insl(int port, void *addr, unsigned long count); | ||
|
|
||
| void insw(int port, void *addr, unsigned long count); | ||
|
|
||
| void insb(int port, void *addr, unsigned long count); | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| #ifndef _TESTS_TEST_H | ||
| #define _TESTS_TEST_H | ||
|
|
||
| /* | ||
| * Standard test header that should be included in all tests. For now it just encapsulates the | ||
| * include dependencies for Cmocka. Test-specific APIs that are so generic we would want them | ||
| * available everywhere could also be added here. | ||
| */ | ||
|
|
||
| #include <arch/types.h> | ||
| #include <stdarg.h> | ||
| #include <stddef.h> | ||
| #include <setjmp.h> | ||
| #include <cmocka.h> | ||
|
|
||
| /* Helper macro to aviud checkpatch errors for some macros */ | ||
| #define EMPTY_WRAP(...) __VA_ARGS__ | ||
|
|
||
| /* | ||
| * Set symbol value and make it global. | ||
| */ | ||
| #define TEST_SYMBOL(symbol, value) asm(".set " #symbol ", " #value "\n\t.globl " #symbol) | ||
|
|
||
| /* | ||
| * Define memory region for testing purpose. | ||
| * | ||
| * Create buffer with specified name and size. | ||
| * Create end symbol for it. | ||
| */ | ||
| #define TEST_REGION(region, size) uint8_t _##region[size]; \ | ||
| TEST_SYMBOL(_e##region, _##region + size); \ | ||
| TEST_SYMBOL(_##region##_size, size) | ||
|
|
||
| /* | ||
| * Set start, end and size symbols describing region without allocating memory for it. | ||
| */ | ||
| #define TEST_REGION_UNALLOCATED(region, start, size) EMPTY_WRAP( \ | ||
| TEST_SYMBOL(_##region, start); \ | ||
| TEST_SYMBOL(_e##region, _##region + size); \ | ||
| TEST_SYMBOL(_##region##_size, size) \ | ||
| ) | ||
|
|
||
| /* Wrapper for running cmocka test groups using name provided by build system in __TEST_NAME__ | ||
| This should be used instead of cmocka_run_group_tests(). If there is a need to use custom | ||
| group name, then please use cmocka_run_group_tests_name(). */ | ||
| #define lp_run_group_tests(group_tests, group_setup, group_teardown) \ | ||
| cmocka_run_group_tests_name((__TEST_NAME__ "(" #group_tests ")"), group_tests, \ | ||
| group_setup, group_teardown) | ||
|
|
||
| #endif /* _TESTS_TEST_H */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,3 @@ ramstage-y += chip_name.c | |
| ramstage-y += model_14_init.c | ||
|
|
||
| subdirs-y += ../../mtrr | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,3 @@ smm-y += udelay.c | |
|
|
||
| subdirs-y += ../../mtrr | ||
| subdirs-y += ../../smm | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,3 @@ ramstage-y += chip_name.c | |
| ramstage-y += model_16_init.c | ||
|
|
||
| subdirs-y += ../../mtrr | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,3 @@ ramstage-y += model_16_init.c | |
| ramstage-y += update_microcode.c | ||
|
|
||
| subdirs-y += ../../mtrr | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,4 @@ | ||
| subdirs-y += ../model_106cx | ||
| subdirs-y += ../microcode | ||
| subdirs-y += ../speedstep | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,4 @@ | ||
| subdirs-y += ../model_1067x | ||
| subdirs-y += ../microcode | ||
| subdirs-y += ../speedstep | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,4 @@ | ||
| subdirs-y += ../model_106cx | ||
| subdirs-y += ../microcode | ||
| subdirs-y += ../speedstep | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,3 @@ bootblock-y += bootblock.c | |
| romstage-y += ../intel/car/romstage.c | ||
|
|
||
| ramstage-y += qemu.c | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,72 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
|
|
||
| /* | ||
| * Pushes a 32-bit register onto the stack. | ||
| * | ||
| * There are two possible code sections where this code can be included: | ||
| * .code32 and .code64 | ||
| * | ||
| * Doing a `push %eax` while in a .code64 section will result in a compiler | ||
| * error. This macro manually pushes the 32-bit register onto the stack so we | ||
| * can share the code between 32 and 64 bit builds. | ||
| */ | ||
| .macro pushr reg:req | ||
| #if ENV_X86_64 | ||
| movl $0, -4(%esp) | ||
| movl \reg, -8(%esp) | ||
| sub $8, %esp | ||
| #else | ||
| push \reg | ||
| #endif | ||
| .endm | ||
|
|
||
| /* Push struct cpu_info */ | ||
| .macro push_cpu_info index=$0 | ||
| pushr \index /* index (size_t) */ | ||
| pushr $0 /* *cpu */ | ||
| .endm | ||
|
|
||
| /* Push struct per_cpu_segment_data */ | ||
| .macro push_per_cpu_segment_data cpu_info_pointer=%esp | ||
| pushr \cpu_info_pointer /* *cpu_info */ | ||
| .endm | ||
|
|
||
| /* | ||
| * Sets the base address in the segment descriptor array. | ||
| * | ||
| * A segment descriptor has the following structure: | ||
| * struct { | ||
| * uint16_t segment_limit_0_15; | ||
| * uint16_t base_address_0_15; | ||
| * uint8_t base_address_16_23; | ||
| * uint8_t attrs[2]; | ||
| * uint8_t base_address_24_31; | ||
| * }; | ||
| * | ||
| * @desc_array: Address of the descriptor table | ||
| * @base: Address to set in the descriptor | ||
| * @desc_index: Index of the descriptor in the table. Defaults to 0. Must be a | ||
| * register if specified. | ||
| * | ||
| * Clobbers %eax, %ebx. | ||
| */ | ||
| .macro set_segment_descriptor_base desc_array:req, base:req, desc_index | ||
| mov \base, %eax | ||
|
|
||
| mov \desc_array, %ebx | ||
|
|
||
| .ifb \desc_index | ||
| movw %ax, 2(%ebx) | ||
| shr $16, %eax | ||
| movb %al, 4(%ebx) | ||
| shr $8, %eax | ||
| movb %al, 7(%ebx) | ||
| .else | ||
| movw %ax, 2(%ebx, \desc_index, 8) | ||
| shr $16, %eax | ||
| movb %al, 4(%ebx, \desc_index, 8) | ||
| shr $8, %eax | ||
| movb %al, 7(%ebx, \desc_index, 8) | ||
| .endif | ||
|
|
||
| .endm |