mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-27 18:25:05 +00:00
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 <victoryang@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/49954 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
This commit is contained in:
@@ -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 )
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
12
board/host/board.c
Normal file
12
board/host/board.c
Normal file
@@ -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},
|
||||
};
|
||||
21
board/host/board.h
Normal file
21
board/host/board.h
Normal file
@@ -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 */
|
||||
11
board/host/build.mk
Normal file
11
board/host/build.mk
Normal file
@@ -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
|
||||
34
board/host/chipset.c
Normal file
34
board/host/chipset.c
Normal file
@@ -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 <stdio.h>
|
||||
#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
|
||||
23
board/host/ec.tasklist
Normal file
23
board/host/ec.tasklist
Normal file
@@ -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)
|
||||
12
chip/host/build.mk
Normal file
12
chip/host/build.mk
Normal file
@@ -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
|
||||
51
chip/host/config.h
Normal file
51
chip/host/config.h
Normal file
@@ -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 */
|
||||
|
||||
24
chip/host/gpio.c
Normal file
24
chip/host/gpio.c
Normal file
@@ -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 */
|
||||
}
|
||||
46
chip/host/keyboard_raw.c
Normal file
46
chip/host/keyboard_raw.c
Normal file
@@ -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
|
||||
}
|
||||
74
chip/host/system.c
Normal file
74
chip/host/system.c
Normal file
@@ -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 <stdlib.h>
|
||||
|
||||
#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 */
|
||||
}
|
||||
86
chip/host/uart.c
Normal file
86
chip/host/uart.c
Normal file
@@ -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 <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
37
core/host/atomic.h
Normal file
37
core/host/atomic.h
Normal file
@@ -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 */
|
||||
11
core/host/build.mk
Normal file
11
core/host/build.mk
Normal file
@@ -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
|
||||
13
core/host/cpu.h
Normal file
13
core/host/cpu.h
Normal file
@@ -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 */
|
||||
11
core/host/disabled.c
Normal file
11
core/host/disabled.c
Normal file
@@ -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));
|
||||
80
core/host/host_exe.lds
Normal file
80
core/host/host_exe.lds
Normal file
@@ -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;
|
||||
18
core/host/main.c
Normal file
18
core/host/main.c
Normal file
@@ -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;
|
||||
}
|
||||
13
core/host/panic.c
Normal file
13
core/host/panic.c
Normal file
@@ -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;
|
||||
}
|
||||
208
core/host/task.c
Normal file
208
core/host/task.c
Normal file
@@ -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 <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
61
core/host/timer.c
Normal file
61
core/host/timer.c
Normal file
@@ -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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
15
include/host_test.h
Normal file
15
include/host_test.h
Normal file
@@ -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 */
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
||||
/* 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.
|
||||
*
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
17
test/utils.c
17
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);
|
||||
|
||||
26
util/run_host_test
Executable file
26
util/run_host_test
Executable file
@@ -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)
|
||||
Reference in New Issue
Block a user