diff --git a/arch/arc/defconfig b/arch/arc/defconfig index 2e763a94dfe..5ee946950c5 100644 --- a/arch/arc/defconfig +++ b/arch/arc/defconfig @@ -13,6 +13,7 @@ CONFIG_SYS_CLOCK_EXISTS=y # CONFIG_INIT_STACKS is not set CONFIG_XIP=y # CONFIG_ENHANCED_SECURITY is not set +# CONFIG_EVENT_LOGGER is not set # # Nanokernel Options diff --git a/arch/arm/defconfig b/arch/arm/defconfig index dd4a4aa55f3..4de6240a08c 100644 --- a/arch/arm/defconfig +++ b/arch/arm/defconfig @@ -14,6 +14,7 @@ CONFIG_SYS_CLOCK_EXISTS=y # CONFIG_INIT_STACKS is not set CONFIG_XIP=y # CONFIG_ENHANCED_SECURITY is not set +# CONFIG_EVENT_LOGGER is not set # # Nanokernel Options diff --git a/arch/x86/defconfig b/arch/x86/defconfig index f084d852f25..9263b3131e0 100644 --- a/arch/x86/defconfig +++ b/arch/x86/defconfig @@ -16,6 +16,7 @@ CONFIG_SYS_CLOCK_EXISTS=y # CONFIG_INIT_STACKS is not set # CONFIG_XIP is not set CONFIG_ENHANCED_SECURITY=y +# CONFIG_EVENT_LOGGER is not set # # Security Options diff --git a/include/misc/event_logger.h b/include/misc/event_logger.h new file mode 100644 index 00000000000..b618c61b192 --- /dev/null +++ b/include/misc/event_logger.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015 Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * @file + * @brief Event logger support. + */ + + +#include +#include + +#ifndef __EVENT_LOGGER_H__ +#define __EVENT_LOGGER_H__ + +#define EVENT_HEADER_SIZE 1 + + +struct event_header_bits { + /* length in 32-bit words */ + uint32_t data_length :4; + uint32_t dropped_count :4; + uint32_t event_id :16; + uint32_t reserved :8; +}; + +union event_header { + uint32_t block; + struct event_header_bits bits; +}; + +struct event_logger { + uint32_t head; + uint32_t tail; + uint16_t dropped_event_count; + struct nano_sem sync_sema; + uint32_t *buffer; + uint32_t buffer_size; +}; + + +/** + * @brief Initialize the event logger. + * + * @details Initialize the ring buffer. + * + * @param logger Logger to be initialized. + * @param logger_buffer Pointer to the buffer to be used by the logger. + * @param buffer_size Size of the buffer in 32-bit words. + * + * @return No return value. + */ +void sys_event_logger_init(struct event_logger *logger, + uint32_t *logger_buffer, uint32_t buffer_size); + + +/** + * @brief Send an event message to the logger. + * + * @details Add an event message to the ring buffer and signal the sync + * semaphore to inform that there are event messages available. + * + * @param logger Pointer to the event logger used. + * @param event_id The identification of the profiler event. + * @param data Pointer to the data of the message. + * @param data_size Size of the buffer in 32-bit words. + * + * @return No return value. + */ +void sys_event_logger_put(struct event_logger *logger, uint16_t event_id, + uint32_t *event_data, uint8_t data_size); + + +/** + * @brief Retrieve an event message from the logger. + * + * @details Retrieve an event message from the ring buffer and copy it to the + * provided buffer. If the provided buffer is smaller than the message + * size the function returns -EMSGSIZE. Otherwise return the number of 32-bit + * words copied. The functon retrieves messages in FIFO order. If there is no + * message in the buffer the fuction returns immediately. It can only be called + * from a fiber context. + * + * @param logger Pointer to the event logger used. + * @param buffer Pointer to the buffer where the message will be copied. + * @param buffer_size Size of the buffer in 32-bit words. + * + * @return -EMSGSIZE if the buffer size is smaller than the message size, or + * the amount of 32-bit words copied. 0 (zero) if there was no message already + * available. + */ +int sys_event_logger_get(struct event_logger *logger, uint32_t *buffer, + uint8_t buffer_size); + + +/** + * @brief Retrieve an event message from the logger, wait if empty. + * + * @details Retrieve an event message from the ring buffer and copy it to the + * provided buffer. If the provided buffer is smaller than the message + * size the function returns -EMSGSIZE. Otherwise return the number of 32-bit + * words copied. The functon retrieves messages in FIFO order. The caller pends + * if there is no message available in the buffer. It can only be called from a + * fiber context. + * + * @param logger Pointer to the event logger used. + * @param buffer Pointer to the buffer where the message will be copied. + * @param buffer_size Size of the buffer in 32-bit words. + * + * @return -EMSGSIZE if the buffer size is smaller than the message size. Or + * the amount of DWORDs copied. + */ +int sys_event_logger_get_wait(struct event_logger *logger, uint32_t *buffer, + uint8_t buffer_size); + +#ifdef CONFIG_NANO_TIMEOUTS +/** + * @brief Retrieve an event message from the logger, wait with a timeout if + * empty. + * + * @details Retrieve an event message from the ring buffer and copy it to the + * provided buffer. If the provided buffer is smaller than the message + * size the function returns -EMSGSIZE. Otherwise return the number of dwords + * copied. The functon retrieves messages in FIFO order. The caller pends if + * there is no message available in the buffer until a new message is added or + * the timeout expires. It can only be called from a fiber context. + * + * @param logger Pointer to the event logger used. + * @param buffer Pointer to the buffer where the message will be copied. + * @param buffer_size Size of the buffer. + * @param timeout Timeout in ticks. + * + * @return -EMSGSIZE if the buffer size is smaller than the message size. Or + * the amount of 32-bit words copied. 0 (zero) if the timeout expired and there + * was no message already available. + */ +int sys_event_logger_get_wait_timeout(struct event_logger *logger, + uint32_t *buffer, uint8_t buffer_size, uint32_t timeout); +#endif /* CONFIG_NANO_TIMEOUTS */ + +#endif /* __EVENT_LOGGER_H__ */ diff --git a/kernel/Kconfig b/kernel/Kconfig index f18b3073244..376ed634705 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -103,6 +103,14 @@ config ENHANCED_SECURITY Users can customize these settings using the CUSTOM_SECURITY option in the "Security Options" menu. +config EVENT_LOGGER + bool + prompt "Enable event logger" + default n + help + Enable event logging feature. Allow the usage of a ring buffer to + transmit event messages with a single interface to collect them. + menu "Security Options" depends on ENHANCED_SECURITY diff --git a/kernel/nanokernel/Makefile b/kernel/nanokernel/Makefile index 12a05a56fe4..2e1576c981d 100644 --- a/kernel/nanokernel/Makefile +++ b/kernel/nanokernel/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_STACK_CANARIES) += compiler_stack_protect.o obj-$(CONFIG_INT_LATENCY_BENCHMARK) += int_latency_bench.o obj-$(CONFIG_ADVANCED_POWER_MANAGEMENT) += idle.o obj-$(CONFIG_NANO_TIMERS) += nano_timer.o +obj-$(CONFIG_EVENT_LOGGER) += event_logger.o diff --git a/kernel/nanokernel/event_logger.c b/kernel/nanokernel/event_logger.c new file mode 100644 index 00000000000..4a886e79899 --- /dev/null +++ b/kernel/nanokernel/event_logger.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2015 Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * @file + * @brief Event logger support. + */ + +#include + + +void sys_event_logger_init(struct event_logger *logger, + uint32_t *logger_buffer, uint32_t buffer_size) +{ + logger->head = logger->tail = 0; + logger->dropped_event_count = 0; + logger->buffer = logger_buffer; + logger->buffer_size = buffer_size; + nano_sem_init(&(logger->sync_sema)); +} + + +static void event_logger_put(struct event_logger *logger, uint16_t event_id, + uint32_t *event_data, uint8_t data_size, + void(*sem_give_fn)(struct nano_sem *)) +{ + unsigned int key; + unsigned int buffer_capacity_used; + union event_header header; + int i; + + /* Lock interrupt to be sure this function will be atomic */ + key = irq_lock_inline(); + + buffer_capacity_used = (logger->head - logger->tail + + logger->buffer_size) % logger->buffer_size; + + /* check if there is space to the new event */ + if (buffer_capacity_used + EVENT_HEADER_SIZE + data_size >= + logger->buffer_size) { + (logger->dropped_event_count)++; + } else { + /* build the header */ + header.bits.data_length = data_size; + header.bits.event_id = event_id; + header.bits.dropped_count = logger->dropped_event_count; + logger->dropped_event_count = 0; + + /* copy the header to the buffer */ + logger->buffer[logger->head] = header.block; + logger->head = (logger->head + EVENT_HEADER_SIZE) % + logger->buffer_size; + + /* copy the extra data to the buffer */ + for (i=0; i < header.bits.data_length; ++i) { + logger->buffer[(logger->head + i) % + logger->buffer_size] = event_data[i]; + } + logger->head = (logger->head + header.bits.data_length) % + logger->buffer_size; + + /* inform that there is event data available on the buffer */ + sem_give_fn(&(logger->sync_sema)); + } + + irq_unlock_inline(key); +} + + +void sys_event_logger_put(struct event_logger *logger, uint16_t event_id, + uint32_t *event_data, uint8_t data_size) +{ + event_logger_put(logger, event_id, event_data, data_size, nano_sem_give); +} + + +/** + * @brief Send an event message to the logger with a non preemptible + * behaviour. + * + * @details Add an event message to the ring buffer and signal the sync + * semaphore using the internal function _sem_give_non_preemptible to inform + * that there are event messages available, avoiding the preemptible + * behaviour when the function is called from a task context. This function + * should be only used for special cases where the sys_event_logger_put + * does not satisfy the needs. + * + * @param logger Pointer to the event logger used. + * @param event_id The identification of the profiler event. + * @param data Pointer to the data of the message. + * @param data_size Size of the buffer in 32-bit words. + * + * @return No return value. + */ +void _sys_event_logger_put_non_preemptible(struct event_logger *logger, + uint16_t event_id, uint32_t *event_data, uint8_t data_size) +{ + extern void _sem_give_non_preemptible(struct nano_sem *sem); + + event_logger_put(logger, event_id, event_data, data_size, + _sem_give_non_preemptible); +} + + +static inline int event_logger_get(struct event_logger *logger, + uint32_t *buffer, uint8_t buffer_size) +{ + int i; + union event_header *header; + + /* obtain the header */ + header = (union event_header *) &(logger->buffer[logger->tail]); + if (buffer_size < EVENT_HEADER_SIZE + header->bits.data_length) { + /* if the user can not retrieve the message, we increase the semaphore + * to indicate that the message remains in the buffer */ + nano_fiber_sem_give(&(logger->sync_sema)); + return -EMSGSIZE; + } + + for (i=0; i < header->bits.data_length + EVENT_HEADER_SIZE; i++) { + buffer[i] = logger->buffer[(logger->tail + i) % + logger->buffer_size]; + } + logger->tail = (logger->tail + header->bits.data_length + + EVENT_HEADER_SIZE) % logger->buffer_size; + + return header->bits.data_length + EVENT_HEADER_SIZE; +} + + +int sys_event_logger_get(struct event_logger *logger, uint32_t *buffer, + uint8_t buffer_size) +{ + if (nano_fiber_sem_take(&(logger->sync_sema))) { + return sys_event_logger_get(logger, buffer, buffer_size); + } + return 0; +} + + +int sys_event_logger_get_wait(struct event_logger *logger, uint32_t *buffer, + uint8_t buffer_size) +{ + nano_fiber_sem_take_wait(&(logger->sync_sema)); + + return event_logger_get(logger, buffer, buffer_size); +} + + +#ifdef CONFIG_NANO_TIMEOUTS +int sys_event_logger_get_wait_timeout(struct event_logger *logger, + uint32_t *buffer, uint8_t buffer_size, uint32_t timeout) +{ + if (nano_fiber_sem_take_wait_timeout(&(logger->sync_sema), timeout)) { + return event_logger_get(logger, buffer, buffer_size); + } + return 0; +} +#endif /* CONFIG_NANO_TIMEOUTS */