From 0a45fa17086d4556b7cb4ea0a9f53894197bc897 Mon Sep 17 00:00:00 2001 From: Vic Yang Date: Thu, 2 May 2013 00:37:07 +0800 Subject: Pthread-based emulator for unit testing This is the first version of pthread-based RTOS emulator. With this, we will be able to test high-level modules entirely on the host machine. BUG=chrome-os-partner:19325 TEST='make runtests' and see tests passing. BRANCH=None Change-Id: I1f5fcd76aa84bdb46c7d35c5e60ae5d92fd3a319 Signed-off-by: Vic Yang Reviewed-on: https://gerrit.chromium.org/gerrit/49954 Reviewed-by: Vincent Palatin --- Makefile.rules | 28 ++++++- Makefile.toolchain | 6 +- board/host/board.c | 12 +++ board/host/board.h | 21 +++++ board/host/build.mk | 11 +++ board/host/chipset.c | 34 ++++++++ board/host/ec.tasklist | 23 +++++ chip/host/build.mk | 12 +++ chip/host/config.h | 51 ++++++++++++ chip/host/gpio.c | 24 ++++++ chip/host/keyboard_raw.c | 46 ++++++++++ chip/host/system.c | 74 +++++++++++++++++ chip/host/uart.c | 86 +++++++++++++++++++ common/main.c | 2 +- common/system_common.c | 2 +- core/cortex-m/link_defs.h | 63 -------------- core/host/atomic.h | 37 +++++++++ core/host/build.mk | 11 +++ core/host/cpu.h | 13 +++ core/host/disabled.c | 11 +++ core/host/host_exe.lds | 80 ++++++++++++++++++ core/host/main.c | 18 ++++ core/host/panic.c | 13 +++ core/host/task.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++ core/host/timer.c | 61 ++++++++++++++ include/host_test.h | 15 ++++ include/link_defs.h | 63 ++++++++++++++ include/task_id.h | 3 + test/build.mk | 3 + test/kb_scan.c | 15 ++-- test/mutex.c | 7 +- test/pingpong.c | 7 +- test/timer_dos.c | 7 +- test/utils.c | 17 ++-- util/run_host_test | 26 ++++++ 35 files changed, 1026 insertions(+), 84 deletions(-) create mode 100644 board/host/board.c create mode 100644 board/host/board.h create mode 100644 board/host/build.mk create mode 100644 board/host/chipset.c create mode 100644 board/host/ec.tasklist create mode 100644 chip/host/build.mk create mode 100644 chip/host/config.h create mode 100644 chip/host/gpio.c create mode 100644 chip/host/keyboard_raw.c create mode 100644 chip/host/system.c create mode 100644 chip/host/uart.c delete mode 100644 core/cortex-m/link_defs.h create mode 100644 core/host/atomic.h create mode 100644 core/host/build.mk create mode 100644 core/host/cpu.h create mode 100644 core/host/disabled.c create mode 100644 core/host/host_exe.lds create mode 100644 core/host/main.c create mode 100644 core/host/panic.c create mode 100644 core/host/task.c create mode 100644 core/host/timer.c create mode 100644 include/host_test.h create mode 100644 include/link_defs.h create mode 100755 util/run_host_test diff --git a/Makefile.rules b/Makefile.rules index e9b0a1802c..4def77851c 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -34,12 +34,14 @@ cmd_flat_to_obj = $(CC) -T $(out)/firmware_image.lds -nostdlib $(CPPFLAGS) \ cmd_elf_to_flat = $(OBJCOPY) -O binary $^ $@ cmd_elf_to_dis = $(OBJDUMP) -D $< > $@ cmd_elf = $(LD) $(LDFLAGS) $(objs) -o $@ -T $< -Map $(out)/$*.map +cmd_exe = $(CC) $(HOST_TEST_LDFLAGS) $(objs) -o $@ cmd_c_to_o = $(CC) $(CFLAGS) -MMD -MF $@.d -c $< -o $@ cmd_c_to_build = $(BUILDCC) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) \ -MMD -MF $@.d $< -o $@ cmd_c_to_host = $(HOSTCC) $(HOST_CFLAGS) -MMD -MF $@.d $(filter %.c, $^) -o $@ cmd_qemu = ./util/run_qemu_test --image=build/$(BOARD)/$*/$*.bin test/$*.py \ $(silent) +cmd_host_test = ./util/run_host_test $* $(silent) cmd_version = ./util/getversion.sh > $@ cmd_mv_from_tmp = mv $(out)/$*.bin.tmp $(out)/$*.bin cmd_extractrw-y = cd $(out) && \ @@ -47,7 +49,7 @@ cmd_extractrw-y = cd $(out) && \ mv RW_SECTION_A $(PROJECT).RW.bin cmd_copyrw-y = cd $(out) && cp $(PROJECT).RW.flat $(PROJECT).RW.bin -.PHONY: all tests utils +.PHONY: all tests utils hosttests all: $(out)/$(PROJECT).bin utils dis-y = $(out)/$(PROJECT).RO.dis $(out)/$(PROJECT).RW.dis @@ -55,9 +57,10 @@ dis: $(dis-y) utils: $(build-utils) $(host-utils) +# On board test binaries test-targets=$(foreach t,$(test-list-y),test-$(t)) qemu-test-targets=$(foreach t,$(test-list-y),qemu-$(t)) -.PHONY: $(qemu-test-target) $(test-targets) +.PHONY: $(qemu-test-targets) $(test-targets) $(test-targets): test-%: @set -e ; \ @@ -72,6 +75,24 @@ $(qemu-test-targets): qemu-%: test-% tests: $(test-targets) qemu-tests: $(qemu-test-targets) +# Emulator test executables +host-test-targets=$(foreach t,$(test-list-host),host-$(t)) +run-test-targets=$(foreach t,$(test-list-host),run-$(t)) +.PHONY: $(host-test-targets) $(run-test-targets) + +$(host-test-targets): host-%: + @set -e ; \ + echo " BUILD host - build/host/$*" ; \ + $(MAKE) --no-print-directory BOARD=host PROJECT=$* \ + V=$(V) out=build/host/$* TEST_BUILD=y EMU_BUILD=y \ + CROSS_COMPILE= build/host/$*/$*.exe + +$(run-test-targets): run-%: host-% + $(call quiet,host_test,TEST ) + +hosttests: $(host-test-targets) +runtests: $(run-test-targets) + $(out)/firmware_image.lds: common/firmware_image.lds.S $(call quiet,lds,LDS ) $(out)/%.lds: core/$(CORE)/ec.lds.S @@ -98,6 +119,9 @@ $(out)/%.flat: $(out)/%.elf $(out)/%.elf: $(out)/%.lds $(objs) $(call quiet,elf,LD ) +$(out)/$(PROJECT).exe: $(objs) + $(call quiet,exe,EXE ) + $(out)/%.o:%.c $(call quiet,c_to_o,CC ) diff --git a/Makefile.toolchain b/Makefile.toolchain index 8b2de15a85..0ec8365d8f 100644 --- a/Makefile.toolchain +++ b/Makefile.toolchain @@ -27,7 +27,8 @@ CFLAGS_WARN=-Wall -Werror -Wundef -Wstrict-prototypes -Wno-trigraphs \ CFLAGS_DEBUG= -g CFLAGS_INCLUDE=$(foreach i,$(includes),-I$(i) ) CFLAGS_TEST=$(if $(TEST_BUILD),-DTEST_BUILD \ - -DTEST_TASKFILE=$(PROJECT).tasklist,) + -DTEST_TASKFILE=$(PROJECT).tasklist,) \ + $(if $(EMU_BUILD),-DEMU_BUILD) CFLAGS_DEFINE=-DOUTDIR=$(out) -DCHIP=$(CHIP) -DBOARD_TASKFILE=ec.tasklist \ -DBOARD=$(BOARD) -DBOARD_$(BOARD) -DCORE=$(CORE) \ -DCHIP_$(CHIP) -DCHIP_VARIANT=$(CHIP_VARIANT) \ @@ -49,3 +50,6 @@ BUILD_CFLAGS= $(LIBFTDI_CFLAGS) $(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN) HOST_CFLAGS=$(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN) LDFLAGS=-nostdlib -X BUILD_LDFLAGS=$(LIBFTDI_LDLIBS) +# For EC emulation on host environment, we need to force 32-bit binary. +# TODO: Fix this. See crosbug.com/p/19257 +HOST_TEST_LDFLAGS=-T core/host/host_exe.lds -m32 -lrt -pthread diff --git a/board/host/board.c b/board/host/board.c new file mode 100644 index 0000000000..a3b8c28aca --- /dev/null +++ b/board/host/board.c @@ -0,0 +1,12 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +/* Emulator board-specific configuration */ + +#include "board.h" +#include "gpio.h" + +const struct gpio_info gpio_list[GPIO_COUNT] = { + {"EC_INT", 0, 0, 0, 0}, +}; diff --git a/board/host/board.h b/board/host/board.h new file mode 100644 index 0000000000..de7049bd9d --- /dev/null +++ b/board/host/board.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Emulator board configuration */ + +#ifndef __BOARD_H +#define __BOARD_H + +#define CONFIG_HOST_EMU +#define CONFIG_HOSTCMD +#define CONFIG_KEYBOARD_PROTOCOL_MKBP + +enum gpio_signal { + GPIO_EC_INT, + + GPIO_COUNT +}; + +#endif /* __BOARD_H */ diff --git a/board/host/build.mk b/board/host/build.mk new file mode 100644 index 0000000000..28525a7679 --- /dev/null +++ b/board/host/build.mk @@ -0,0 +1,11 @@ +# -*- makefile -*- +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Board specific files build +# + +CHIP:=host + +board-y=board.o chipset.o diff --git a/board/host/chipset.c b/board/host/chipset.c new file mode 100644 index 0000000000..b06e293778 --- /dev/null +++ b/board/host/chipset.c @@ -0,0 +1,34 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Chipset module for emulator */ + +#include +#include "chipset.h" +#include "common.h" +#include "task.h" + +test_mockable void chipset_reset(int cold_reset) +{ + fprintf(stderr, "Chipset reset!\n"); +} + +test_mockable void chipset_force_shutdown(void) +{ + /* Do nothing */ +} + +#ifdef HAS_TASK_CHIPSET +test_mockable int chipset_in_state(int state_mask) +{ + return state_mask & CHIPSET_STATE_SOFT_OFF; +} + +test_mockable void chipset_task(void) +{ + while (1) + task_wait_event(-1); +} +#endif diff --git a/board/host/ec.tasklist b/board/host/ec.tasklist new file mode 100644 index 0000000000..d0597f8544 --- /dev/null +++ b/board/host/ec.tasklist @@ -0,0 +1,23 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_ALWAYS(n, r, d, s) for base tasks and + * TASK_NOTEST(n, r, d, s) for tasks that can be excluded in test binaries, + * where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ + +#define CONFIG_TASK_LIST \ + TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) diff --git a/chip/host/build.mk b/chip/host/build.mk new file mode 100644 index 0000000000..ddcbefdb0b --- /dev/null +++ b/chip/host/build.mk @@ -0,0 +1,12 @@ +# -*- makefile -*- +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# emulator specific files build +# + +CORE:=host + +chip-y=system.o gpio.o uart.o +chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o diff --git a/chip/host/config.h b/chip/host/config.h new file mode 100644 index 0000000000..7f2df4e467 --- /dev/null +++ b/chip/host/config.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Chip config header file */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +/* Memory mapping */ +#define CONFIG_FLASH_BASE 0x08000000 +#define CONFIG_FLASH_PHYSICAL_SIZE 0x00020000 +#define CONFIG_FLASH_SIZE CONFIG_FLASH_PHYSICAL_SIZE +#define CONFIG_FLASH_BANK_SIZE 0x1000 +#define CONFIG_FLASH_ERASE_SIZE 0x0400 /* erase bank size */ +#define CONFIG_FLASH_WRITE_SIZE 0x0002 /* minimum write size */ +#define CONFIG_RAM_BASE 0x20000000 +#define CONFIG_RAM_SIZE 0x00002000 + +/* Size of one firmware image in flash */ +#define CONFIG_FW_IMAGE_SIZE (64 * 1024) + +#define CONFIG_FW_RO_OFF 0 +#define CONFIG_FW_RO_SIZE (CONFIG_FW_IMAGE_SIZE \ + - CONFIG_SECTION_FLASH_PSTATE_SIZE) +#define CONFIG_FW_RW_OFF CONFIG_FW_IMAGE_SIZE +#define CONFIG_FW_RW_SIZE CONFIG_FW_IMAGE_SIZE + +#define CONFIG_SECTION_RO_OFF CONFIG_FW_RO_OFF +#define CONFIG_SECTION_RO_SIZE CONFIG_FW_RO_SIZE +#define CONFIG_SECTION_RW_OFF CONFIG_FW_RW_OFF +#define CONFIG_SECTION_RW_SIZE CONFIG_FW_RW_SIZE +#define CONFIG_SECTION_WP_RO_OFF CONFIG_FW_RO_OFF +#define CONFIG_SECTION_WP_RO_SIZE CONFIG_FW_IMAGE_SIZE + +/* + * Put this after RO to give RW more space. This also makes RO write protect + * region contiguous. + */ +#define CONFIG_SECTION_FLASH_PSTATE_SIZE (1 * CONFIG_FLASH_BANK_SIZE) +#define CONFIG_SECTION_FLASH_PSTATE_OFF CONFIG_FW_RO_OFF + CONFIG_FW_RO_SIZE + +/* Maximum number of deferrable functions */ +#define DEFERRABLE_MAX_COUNT 8 + +/* Interval between HOOK_TICK notifications */ +#define HOOK_TICK_INTERVAL (250 * MSEC) + +#endif /* __CONFIG_H */ + diff --git a/chip/host/gpio.c b/chip/host/gpio.c new file mode 100644 index 0000000000..03cfaa4aed --- /dev/null +++ b/chip/host/gpio.c @@ -0,0 +1,24 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* GPIO module for emulator */ + +#include "common.h" +#include "gpio.h" + +test_mockable void gpio_pre_init(void) +{ + /* Nothing */ +} + +test_mockable int gpio_get_level(enum gpio_signal signal) +{ + return 0; +} + +test_mockable void gpio_set_level(enum gpio_signal signal, int value) +{ + /* Nothing */ +} diff --git a/chip/host/keyboard_raw.c b/chip/host/keyboard_raw.c new file mode 100644 index 0000000000..561a72aafa --- /dev/null +++ b/chip/host/keyboard_raw.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Raw keyboard I/O layer for emulator */ + +#include "common.h" +#include "keyboard_config.h" +#include "keyboard_raw.h" +#include "keyboard_scan.h" +#include "task.h" +#include "util.h" + +test_mockable void keyboard_raw_init(void) +{ + /* Nothing */ +} + +test_mockable void keyboard_raw_task_start(void) +{ + /* Nothing */ +} + +test_mockable void keyboard_raw_drive_column(int out) +{ + /* Nothing */ +} + +test_mockable int keyboard_raw_read_rows(void) +{ + /* Nothing pressed */ + return 0; +} + +test_mockable void keyboard_raw_enable_interrupt(int enable) +{ + /* Nothing */ +} + +test_mockable void keyboard_raw_gpio_interrupt(enum gpio_signal signal) +{ +#ifdef HAS_TASK_KEYSCAN + task_wake(TASK_ID_KEYSCAN); +#endif +} diff --git a/chip/host/system.c b/chip/host/system.c new file mode 100644 index 0000000000..50da8db9c0 --- /dev/null +++ b/chip/host/system.c @@ -0,0 +1,74 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* System module for emulator */ + +#include + +#include "host_test.h" +#include "system.h" + +#define SHARED_MEM_SIZE 512 /* bytes */ +char __shared_mem_buf[SHARED_MEM_SIZE]; + +test_mockable void system_reset(int flags) +{ + exit(EXIT_CODE_RESET | flags); +} + +test_mockable void system_hibernate(uint32_t seconds, uint32_t microseconds) +{ + exit(EXIT_CODE_HIBERNATE); +} + +test_mockable int system_is_locked(void) +{ + return 0; +} + +test_mockable int system_jumped_to_this_image(void) +{ + return 0; +} + +test_mockable uint32_t system_get_reset_flags(void) +{ + return RESET_FLAG_POWER_ON; +} + +const char *system_get_chip_vendor(void) +{ + return "chromeos"; +} + +const char *system_get_chip_name(void) +{ + return "emu"; +} + +const char *system_get_chip_revision(void) +{ + return ""; +} + +int system_get_vbnvcontext(uint8_t *block) +{ + return EC_ERROR_UNIMPLEMENTED; +} + +int system_set_vbnvcontext(const uint8_t *block) +{ + return EC_ERROR_UNIMPLEMENTED; +} + +int system_usable_ram_end(void) +{ + return (int)(__shared_mem_buf + SHARED_MEM_SIZE); +} + +void system_pre_init(void) +{ + /* Nothing */ +} diff --git a/chip/host/uart.c b/chip/host/uart.c new file mode 100644 index 0000000000..cbae79974b --- /dev/null +++ b/chip/host/uart.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* UART driver for emulator */ + +#include + +#include "board.h" +#include "config.h" +#include "task.h" +#include "uart.h" + +static int stopped; +static int int_disabled; +static int init_done; + +static void trigger_interrupt(void) +{ + if (!int_disabled) + uart_process(); +} + +int uart_init_done(void) +{ + return init_done; +} + +void uart_tx_start(void) +{ + stopped = 0; + trigger_interrupt(); +} + +void uart_tx_stop(void) +{ + stopped = 1; +} + +int uart_tx_stopped(void) +{ + return stopped; +} + +void uart_tx_flush(void) +{ + /* Nothing */ +} + +int uart_tx_ready(void) +{ + return 1; +} + +int uart_rx_available(void) +{ + return 0; +} + +void uart_write_char(char c) +{ + printf("%c", c); + fflush(stdout); +} + +int uart_read_char(void) +{ + /* Should never be called for now */ + return 0; +} + +void uart_disable_interrupt(void) +{ + int_disabled = 1; +} + +void uart_enable_interrupt(void) +{ + int_disabled = 0; +} + +void uart_init(void) +{ + init_done = 1; +} diff --git a/common/main.c b/common/main.c index bd1b6f4543..9a5a75f1cd 100644 --- a/common/main.c +++ b/common/main.c @@ -27,7 +27,7 @@ #define CPUTS(outstr) cputs(CC_SYSTEM, outstr) #define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) -int main(void) +test_mockable int main(void) { /* * Pre-initialization (pre-verified boot) stage. Initialization at diff --git a/common/system_common.c b/common/system_common.c index dbb83303b1..239dd12a75 100644 --- a/common/system_common.c +++ b/common/system_common.c @@ -104,7 +104,7 @@ int system_is_locked(void) #endif } -int system_usable_ram_end(void) +test_mockable int system_usable_ram_end(void) { /* Leave space at the end of RAM for jump data and tags. * diff --git a/core/cortex-m/link_defs.h b/core/cortex-m/link_defs.h deleted file mode 100644 index 6681d6b371..0000000000 --- a/core/cortex-m/link_defs.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * Symbols from linker definitions - */ - -#ifndef __CROS_EC_LINK_DEFS_H -#define __CROS_EC_LINK_DEFS_H - -#include "console.h" -#include "hooks.h" -#include "host_command.h" -#include "task.h" - -/* Console commands */ -extern const struct console_command __cmds[]; -extern const struct console_command __cmds_end[]; - -/* Hooks */ -extern const struct hook_data __hooks_init[]; -extern const struct hook_data __hooks_init_end[]; -extern const struct hook_data __hooks_freq_change[]; -extern const struct hook_data __hooks_freq_change_end[]; -extern const struct hook_data __hooks_sysjump[]; -extern const struct hook_data __hooks_sysjump_end[]; -extern const struct hook_data __hooks_chipset_pre_init[]; -extern const struct hook_data __hooks_chipset_pre_init_end[]; -extern const struct hook_data __hooks_chipset_startup[]; -extern const struct hook_data __hooks_chipset_startup_end[]; -extern const struct hook_data __hooks_chipset_resume[]; -extern const struct hook_data __hooks_chipset_resume_end[]; -extern const struct hook_data __hooks_chipset_suspend[]; -extern const struct hook_data __hooks_chipset_suspend_end[]; -extern const struct hook_data __hooks_chipset_shutdown[]; -extern const struct hook_data __hooks_chipset_shutdown_end[]; -extern const struct hook_data __hooks_ac_change[]; -extern const struct hook_data __hooks_ac_change_end[]; -extern const struct hook_data __hooks_lid_change[]; -extern const struct hook_data __hooks_lid_change_end[]; -extern const struct hook_data __hooks_pwrbtn_change[]; -extern const struct hook_data __hooks_pwrbtn_change_end[]; -extern const struct hook_data __hooks_tick[]; -extern const struct hook_data __hooks_tick_end[]; -extern const struct hook_data __hooks_second[]; -extern const struct hook_data __hooks_second_end[]; - -/* Deferrable functions */ -extern const struct deferred_data __deferred_funcs[]; -extern const struct deferred_data __deferred_funcs_end[]; - -/* Host commands */ -extern const struct host_command __hcmds[]; -extern const struct host_command __hcmds_end[]; - -/* IRQs (interrupt handlers) */ -extern const struct irq_priority __irqprio[]; -extern const struct irq_priority __irqprio_end[]; - -/* Shared memory buffer. Use via shared_mem.h interface. */ -extern uint8_t __shared_mem_buf[]; - -#endif /* __CROS_EC_LINK_DEFS_H */ diff --git a/core/host/atomic.h b/core/host/atomic.h new file mode 100644 index 0000000000..77e9cc6ba8 --- /dev/null +++ b/core/host/atomic.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Atomic operations for emulator */ + +#ifndef __CROS_EC_ATOMIC_H +#define __CROS_EC_ATOMIC_H + +#include "common.h" + +static inline void atomic_clear(uint32_t *addr, uint32_t bits) +{ + __sync_and_and_fetch(addr, ~bits); +} + +static inline void atomic_or(uint32_t *addr, uint32_t bits) +{ + __sync_or_and_fetch(addr, bits); +} + +static inline void atomic_add(uint32_t *addr, uint32_t value) +{ + __sync_add_and_fetch(addr, value); +} + +static inline void atomic_sub(uint32_t *addr, uint32_t value) +{ + __sync_sub_and_fetch(addr, value); +} + +static inline uint32_t atomic_read_clear(uint32_t *addr) +{ + return __sync_fetch_and_and(addr, 0); +} +#endif /* __CROS_EC_ATOMIC_H */ diff --git a/core/host/build.mk b/core/host/build.mk new file mode 100644 index 0000000000..879b9266fa --- /dev/null +++ b/core/host/build.mk @@ -0,0 +1,11 @@ +# -*- makefile -*- +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# emulator specific files build +# + +CFLAGS_CPU=-fno-builtin -m32 + +core-y=main.o task.o timer.o panic.o disabled.o diff --git a/core/host/cpu.h b/core/host/cpu.h new file mode 100644 index 0000000000..4e43da623f --- /dev/null +++ b/core/host/cpu.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* CPU specific header file */ + +#ifndef __CPU_H +#define __CPU_H + +static inline void cpu_init(void) { } + +#endif /* __CPU_H */ diff --git a/core/host/disabled.c b/core/host/disabled.c new file mode 100644 index 0000000000..679a1ab451 --- /dev/null +++ b/core/host/disabled.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Disabled functions */ + +#define DISABLED(proto) proto { } + +DISABLED(void jtag_pre_init(void)); +DISABLED(void clock_init(void)); diff --git a/core/host/host_exe.lds b/core/host/host_exe.lds new file mode 100644 index 0000000000..46a5097d67 --- /dev/null +++ b/core/host/host_exe.lds @@ -0,0 +1,80 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +SECTIONS { + .rodata.ec_sections : { + /* Symbols defined here are declared in link_defs.h */ + __irqprio = .; + *(.rodata.irqprio) + __irqprio_end = .; + + . = ALIGN(4); + __cmds = .; + *(SORT(.rodata.cmds*)) + __cmds_end = .; + + . = ALIGN(4); + __hcmds = .; + *(.rodata.hcmds) + __hcmds_end = .; + + . = ALIGN(4); + __hooks_init = .; + *(.rodata.HOOK_INIT) + __hooks_init_end = .; + + __hooks_freq_change = .; + *(.rodata.HOOK_FREQ_CHANGE) + __hooks_freq_change_end = .; + + __hooks_sysjump = .; + *(.rodata.HOOK_SYSJUMP) + __hooks_sysjump_end = .; + + __hooks_chipset_pre_init = .; + *(.rodata.HOOK_CHIPSET_PRE_INIT) + __hooks_chipset_pre_init_end = .; + + __hooks_chipset_startup = .; + *(.rodata.HOOK_CHIPSET_STARTUP) + __hooks_chipset_startup_end = .; + + __hooks_chipset_resume = .; + *(.rodata.HOOK_CHIPSET_RESUME) + __hooks_chipset_resume_end = .; + + __hooks_chipset_suspend = .; + *(.rodata.HOOK_CHIPSET_SUSPEND) + __hooks_chipset_suspend_end = .; + + __hooks_chipset_shutdown = .; + *(.rodata.HOOK_CHIPSET_SHUTDOWN) + __hooks_chipset_shutdown_end = .; + + __hooks_ac_change = .; + *(.rodata.HOOK_AC_CHANGE) + __hooks_ac_change_end = .; + + __hooks_lid_change = .; + *(.rodata.HOOK_LID_CHANGE) + __hooks_lid_change_end = .; + + __hooks_pwrbtn_change = .; + *(.rodata.HOOK_POWER_BUTTON_CHANGE) + __hooks_pwrbtn_change_end = .; + + __hooks_tick = .; + *(.rodata.HOOK_TICK) + __hooks_tick_end = .; + + __hooks_second = .; + *(.rodata.HOOK_SECOND) + __hooks_second_end = .; + + __deferred_funcs = .; + *(.rodata.deferred) + __deferred_funcs_end = .; + } +} +INSERT BEFORE .rodata; diff --git a/core/host/main.c b/core/host/main.c new file mode 100644 index 0000000000..f370e4b561 --- /dev/null +++ b/core/host/main.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Entry point of unit test executable */ + +#include "task.h" +#include "timer.h" + +int main(void) +{ + timer_init(); + + task_start(); + + return 0; +} diff --git a/core/host/panic.c b/core/host/panic.c new file mode 100644 index 0000000000..90ddf55dd7 --- /dev/null +++ b/core/host/panic.c @@ -0,0 +1,13 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "common.h" +#include "panic.h" +#include "util.h" + +struct panic_data *panic_get_data(void) +{ + return NULL; +} diff --git a/core/host/task.c b/core/host/task.c new file mode 100644 index 0000000000..caad622778 --- /dev/null +++ b/core/host/task.c @@ -0,0 +1,208 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Task scheduling / events module for Chrome EC operating system */ + +#include +#include +#include + +#include "atomic.h" +#include "common.h" +#include "task.h" +#include "task_id.h" +#include "timer.h" + +struct emu_task_t { + pthread_t thread; + pthread_cond_t resume; + uint32_t event; + timestamp_t wake_time; +}; + +struct task_args { + void (*routine)(void *); + void *d; +}; + +static struct emu_task_t tasks[TASK_ID_COUNT]; +static pthread_cond_t scheduler_cond; +static pthread_mutex_t run_lock; + +static __thread task_id_t my_task_id; /* thread local task id */ + +#define TASK(n, r, d, s) void r(void *); +CONFIG_TASK_LIST +CONFIG_TEST_TASK_LIST +#undef TASK + +/* Idle task */ +void __idle(void *d) +{ + while (1) + task_wait_event(-1); +} + +/* Weak reference function as an entry point for unit test */ +test_mockable void run_test(void) { } + +void _run_test(void *d) +{ + run_test(); +} + +#define TASK(n, r, d, s) {r, d}, +struct task_args task_info[TASK_ID_COUNT] = { + {__idle, NULL}, + CONFIG_TASK_LIST + CONFIG_TEST_TASK_LIST + {_run_test, NULL}, +}; +#undef TASK + +#define TASK(n, r, d, s) #n, +static const char * const task_names[] = { + "<< idle >>", + CONFIG_TASK_LIST + CONFIG_TEST_TASK_LIST + "<< test runner >>", +}; +#undef TASK + +void task_pre_init(void) +{ + /* Nothing */ +} + +int in_interrupt_context(void) +{ + return 0; /* No interrupt support yet */ +} + +void interrupt_disable(void) +{ + /* Not supported yet */ +} + +void interrupt_enable(void) +{ + /* Not supported yet */ +} + +uint32_t task_set_event(task_id_t tskid, uint32_t event, int wait) +{ + tasks[tskid].event = event; + if (wait) + return task_wait_event(-1); + return 0; +} + +uint32_t task_wait_event(int timeout_us) +{ + int tid = task_get_current(); + int ret; + if (timeout_us > 0) + tasks[tid].wake_time.val = get_time().val + timeout_us; + pthread_cond_signal(&scheduler_cond); + pthread_cond_wait(&tasks[tid].resume, &run_lock); + ret = tasks[tid].event; + tasks[tid].event = 0; + return ret; +} + +void mutex_lock(struct mutex *mtx) +{ + int value = 0; + int id = 1 << task_get_current(); + + mtx->waiters |= id; + + do { + if (mtx->lock == 0) { + mtx->lock = 1; + value = 1; + } + + if (!value) + task_wait_event(-1); + } while (!value); + + mtx->waiters &= ~id; +} + +void mutex_unlock(struct mutex *mtx) +{ + int v; + mtx->lock = 0; + + for (v = 31; v >= 0; --v) + if ((1ul << v) & mtx->waiters) { + mtx->waiters &= ~(1ul << v); + task_set_event(v, TASK_EVENT_MUTEX, 0); + break; + } +} + +task_id_t task_get_current(void) +{ + return my_task_id; +} + +void task_scheduler(void) +{ + int i; + timestamp_t now; + + while (1) { + now = get_time(); + i = TASK_ID_COUNT - 1; + while (i >= 0) { + if (tasks[i].event || now.val >= tasks[i].wake_time.val) + break; + --i; + } + if (i < 0) + i = TASK_ID_IDLE; + + tasks[i].wake_time.val = ~0ull; + pthread_cond_signal(&tasks[i].resume); + pthread_cond_wait(&scheduler_cond, &run_lock); + } +} + +void *_task_start_impl(void *a) +{ + long tid = (long)a; + struct task_args *arg = task_info + tid; + my_task_id = tid; + pthread_mutex_lock(&run_lock); + tasks[tid].event = 0; + (arg->routine)(arg->d); + while (1) + task_wait_event(-1); +} + +int task_start(void) +{ + int i; + + pthread_mutex_init(&run_lock, NULL); + pthread_cond_init(&scheduler_cond, NULL); + + pthread_mutex_lock(&run_lock); + + for (i = 0; i < TASK_ID_COUNT; ++i) { + tasks[i].event = TASK_EVENT_WAKE; + tasks[i].wake_time.val = ~0ull; + pthread_cond_init(&tasks[i].resume, NULL); + pthread_create(&tasks[i].thread, NULL, _task_start_impl, + (void *)(size_t)i); + pthread_cond_wait(&scheduler_cond, &run_lock); + } + + task_scheduler(); + + return 0; +} diff --git a/core/host/timer.c b/core/host/timer.c new file mode 100644 index 0000000000..8de12a7f3c --- /dev/null +++ b/core/host/timer.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Timer module */ + +#include +#include +#include + +#include "task.h" +#include "timer.h" + +static timestamp_t boot_time; + +void usleep(unsigned us) +{ + task_wait_event(us); +} + +timestamp_t _get_time(void) +{ + struct timespec ts; + timestamp_t ret; + clock_gettime(CLOCK_MONOTONIC, &ts); + ret.val = 1000000 * (uint64_t)ts.tv_sec + ts.tv_nsec / 1000; + return ret; +} + +timestamp_t get_time(void) +{ + timestamp_t ret = _get_time(); + ret.val -= boot_time.val; + return ret; +} + +void udelay(unsigned us) +{ + timestamp_t deadline = get_time(); + deadline.val += us; + while (get_time().val < deadline.val) + ; +} + +int timestamp_expired(timestamp_t deadline, const timestamp_t *now) +{ + timestamp_t now_val; + + if (!now) { + now_val = get_time(); + now = &now_val; + } + + return ((int64_t)(now->val - deadline.val) >= 0); +} + +void timer_init(void) +{ + boot_time = _get_time(); +} diff --git a/include/host_test.h b/include/host_test.h new file mode 100644 index 0000000000..0c1b95d1fa --- /dev/null +++ b/include/host_test.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Unit testing for Chrome EC */ + +#ifndef __CROS_EC_HOST_TEST_H +#define __CROS_EC_HOST_TEST_H + +/* Emulator exit codes */ +#define EXIT_CODE_RESET (1 << 6) /* Leave six bits for SYSTEM_RESET_* */ +#define EXIT_CODE_HIBERNATE (1 << 7) + +#endif /* __CROS_EC_HOST_TEST_H */ diff --git a/include/link_defs.h b/include/link_defs.h new file mode 100644 index 0000000000..a8c54d4afe --- /dev/null +++ b/include/link_defs.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Symbols from linker definitions + */ + +#ifndef __CROS_EC_LINK_DEFS_H +#define __CROS_EC_LINK_DEFS_H + +#include "console.h" +#include "hooks.h" +#include "host_command.h" +#include "task.h" + +/* Console commands */ +extern const struct console_command __cmds[]; +extern const struct console_command __cmds_end[]; + +/* Hooks */ +extern const struct hook_data __hooks_init[]; +extern const struct hook_data __hooks_init_end[]; +extern const struct hook_data __hooks_freq_change[]; +extern const struct hook_data __hooks_freq_change_end[]; +extern const struct hook_data __hooks_sysjump[]; +extern const struct hook_data __hooks_sysjump_end[]; +extern const struct hook_data __hooks_chipset_pre_init[]; +extern const struct hook_data __hooks_chipset_pre_init_end[]; +extern const struct hook_data __hooks_chipset_startup[]; +extern const struct hook_data __hooks_chipset_startup_end[]; +extern const struct hook_data __hooks_chipset_resume[]; +extern const struct hook_data __hooks_chipset_resume_end[]; +extern const struct hook_data __hooks_chipset_suspend[]; +extern const struct hook_data __hooks_chipset_suspend_end[]; +extern const struct hook_data __hooks_chipset_shutdown[]; +extern const struct hook_data __hooks_chipset_shutdown_end[]; +extern const struct hook_data __hooks_ac_change[]; +extern const struct hook_data __hooks_ac_change_end[]; +extern const struct hook_data __hooks_lid_change[]; +extern const struct hook_data __hooks_lid_change_end[]; +extern const struct hook_data __hooks_pwrbtn_change[]; +extern const struct hook_data __hooks_pwrbtn_change_end[]; +extern const struct hook_data __hooks_tick[]; +extern const struct hook_data __hooks_tick_end[]; +extern const struct hook_data __hooks_second[]; +extern const struct hook_data __hooks_second_end[]; + +/* Deferrable functions */ +extern const struct deferred_data __deferred_funcs[]; +extern const struct deferred_data __deferred_funcs_end[]; + +/* Host commands */ +extern const struct host_command __hcmds[]; +extern const struct host_command __hcmds_end[]; + +/* IRQs (interrupt handlers) */ +extern const struct irq_priority __irqprio[]; +extern const struct irq_priority __irqprio_end[]; + +/* Shared memory buffer. Use via shared_mem.h interface. */ +extern uint8_t __shared_mem_buf[]; + +#endif /* __CROS_EC_LINK_DEFS_H */ diff --git a/include/task_id.h b/include/task_id.h index 33445aee9c..985c39848c 100644 --- a/include/task_id.h +++ b/include/task_id.h @@ -47,6 +47,9 @@ enum { CONFIG_TASK_LIST /* CONFIG_TEST_TASK_LIST is a macro from the TEST_TASK_LIST file */ CONFIG_TEST_TASK_LIST +#ifdef EMU_BUILD + TASK_ID_TEST_RUNNER, +#endif /* Number of tasks */ TASK_ID_COUNT, /* Special task identifiers */ diff --git a/test/build.mk b/test/build.mk index 7adf96f441..2c61e80e7d 100644 --- a/test/build.mk +++ b/test/build.mk @@ -24,6 +24,9 @@ test-list-$(BOARD_spring)+=kb_scan flash stress test-list-$(BOARD_link)= test-list-$(BOARD_slippy)= +# Emulator tests +test-list-host=mutex pingpong utils kb_scan + flash-y=flash.o kb_mkbp-y=kb_mkbp.o kb_scan-y=kb_scan.o diff --git a/test/kb_scan.c b/test/kb_scan.c index 32822d689d..c242c43e09 100644 --- a/test/kb_scan.c +++ b/test/kb_scan.c @@ -257,7 +257,7 @@ int lid_test(void) } #endif -static int command_run_test(int argc, char **argv) +void run_test(void) { error_count = 0; lid_open = 1; @@ -268,13 +268,16 @@ static int command_run_test(int argc, char **argv) RUN_TEST(lid_test); #endif - if (error_count == 0) { + if (error_count == 0) ccprintf("Pass!\n"); - return EC_SUCCESS; - } else { + else ccprintf("Fail!\n"); - return EC_ERROR_UNKNOWN; - } +} + +static int command_run_test(int argc, char **argv) +{ + run_test(); + return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(runtest, command_run_test, NULL, NULL, NULL); diff --git a/test/mutex.c b/test/mutex.c index 47f7fa97c4..bc20062d89 100644 --- a/test/mutex.c +++ b/test/mutex.c @@ -114,9 +114,14 @@ int mutex_main_task(void *unused) return EC_SUCCESS; } -static int command_run_test(int argc, char **argv) +void run_test(void) { task_wake(TASK_ID_MTX1); +} + +static int command_run_test(int argc, char **argv) +{ + run_test(); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(runtest, command_run_test, diff --git a/test/pingpong.c b/test/pingpong.c index f9266da26e..4cf5d53ced 100644 --- a/test/pingpong.c +++ b/test/pingpong.c @@ -58,10 +58,15 @@ int TaskTick(void *data) return EC_SUCCESS; } -static int command_run_test(int argc, char **argv) +void run_test(void) { task_wake(TASK_ID_TICK); task_wake(TASK_ID_TESTA); +} + +static int command_run_test(int argc, char **argv) +{ + run_test(); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(runtest, command_run_test, diff --git a/test/timer_dos.c b/test/timer_dos.c index 5ac7cad5df..327206e6df 100644 --- a/test/timer_dos.c +++ b/test/timer_dos.c @@ -46,12 +46,17 @@ int TaskTimer(void *seed) return EC_SUCCESS; } -static int command_run_test(int argc, char **argv) +void run_test(void) { task_wake(TASK_ID_TMRD); task_wake(TASK_ID_TMRC); task_wake(TASK_ID_TMRB); task_wake(TASK_ID_TMRA); +} + +static int command_run_test(int argc, char **argv) +{ + run_test(); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(runtest, command_run_test, diff --git a/test/utils.c b/test/utils.c index 1c04c0d06d..559e8a0d94 100644 --- a/test/utils.c +++ b/test/utils.c @@ -89,7 +89,7 @@ static int test_shared_mem(void) for (i = 0; i < 256; ++i) { memset(mem, i, sz); for (j = 0; j < sz; ++j) - TEST_ASSERT(mem[j] == i); + TEST_ASSERT(mem[j] == (char)i); if ((i & 0xf) == 0) msleep(20); /* Yield to other tasks */ @@ -100,7 +100,7 @@ static int test_shared_mem(void) return EC_SUCCESS; } -static int command_run_test(int argc, char **argv) +void run_test(void) { error_count = 0; @@ -111,13 +111,16 @@ static int command_run_test(int argc, char **argv) RUN_TEST(test_uint64divmod); RUN_TEST(test_shared_mem); - if (error_count) { + if (error_count) ccprintf("Failed %d tests!\n", error_count); - return EC_ERROR_UNKNOWN; - } else { + else ccprintf("Pass!\n"); - return EC_SUCCESS; - } +} + +static int command_run_test(int argc, char **argv) +{ + run_test(); + return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(runtest, command_run_test, NULL, NULL, NULL); diff --git a/util/run_host_test b/util/run_host_test new file mode 100755 index 0000000000..bf8d8efd97 --- /dev/null +++ b/util/run_host_test @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import pexpect +import sys + +TIMEOUT=10 + +test_name = sys.argv[1] +child = pexpect.spawn('build/host/{0}/{0}.exe'.format(test_name), + timeout=TIMEOUT) +child.logfile = sys.stdout +result_id = child.expect([pexpect.TIMEOUT, 'Pass!', 'Fail!']) +if result_id == 0: + sys.stderr.write('Test %s timed out after %d seconds!\n' % + (test_name, TIMEOUT)) + sys.exit(1) +elif result_id == 1: + sys.stderr.write('Test %s passed!\n' % test_name) + sys.exit(0) +elif result_id == 2: + sys.stderr.write('Test %s failed!\n' % test_name) + sys.exit(1) -- cgit v1.2.1