zephyr/drivers/console/winstream_console.c

90 lines
2 KiB
C
Raw Permalink Normal View History

/*
* Copyright (c) 2022 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/winstream.h>
#include <zephyr/devicetree.h>
#include <zephyr/cache.h>
#include <adsp_memory.h>
#include <mem_window.h>
struct k_spinlock trace_lock;
static struct sys_winstream *winstream;
soc/intel_adsp: Robustify logging code The existing implementation of the adsplog.py script worked fine for individual runs (e.g. when running specific code) but had no support for detecting system reset events and thus could not be used for monitoring applications like test automation. It also could not handle the case where a rapid log burst would overflow the buffer before being noticed at the client. Also, the protocol here was also rife with opportunities for race conditions. Fix all that up via what is mostly a rewrite of the script. The protocol itself hasn't changed, just the handling. Also includes some changes to the trace_out.c code on the device side. These are required to get ordering correct to make race conditions tractably handleable on the reader side. Some of the specific cases that are managed: * There is a 0.4s backoff when a reset is detected. Continuing to poll the buffer has been observed to hang the device (I'm fairly sure this is actually a hardware bug, reads aren't visible to the DSP software). * The "no magic number" case needs to be reserved for detecting system reset. * Slot data must be read BETWEEN two reads of the ID value to detect the case where the slot gets clobbered while being read. * The "currently being filled" slot needs to always have an ID value that does not appear in sequence from the prior slot. * We need to check the full history in the buffer at each poll to detect resets, which opens up a race between the read of the "next slot" (which is absent) and the full history retrieval (when it can now be present!). Detect that. * A null termination bug in the current output slot got fixed. Broadly: this was a huge bear to make work. It sounds like this should be a simple protocol, but it's not in practice. Also: clean up the error reporting in the script so it can handle new PCI IDs being added, and reports permissions failures on the required sysfs file as a human-readable error. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2020-12-16 21:47:02 +01:00
void winstream_console_trace_out(int8_t *str, size_t len)
{
soc/intel_adsp: Robustify logging code The existing implementation of the adsplog.py script worked fine for individual runs (e.g. when running specific code) but had no support for detecting system reset events and thus could not be used for monitoring applications like test automation. It also could not handle the case where a rapid log burst would overflow the buffer before being noticed at the client. Also, the protocol here was also rife with opportunities for race conditions. Fix all that up via what is mostly a rewrite of the script. The protocol itself hasn't changed, just the handling. Also includes some changes to the trace_out.c code on the device side. These are required to get ordering correct to make race conditions tractably handleable on the reader side. Some of the specific cases that are managed: * There is a 0.4s backoff when a reset is detected. Continuing to poll the buffer has been observed to hang the device (I'm fairly sure this is actually a hardware bug, reads aren't visible to the DSP software). * The "no magic number" case needs to be reserved for detecting system reset. * Slot data must be read BETWEEN two reads of the ID value to detect the case where the slot gets clobbered while being read. * The "currently being filled" slot needs to always have an ID value that does not appear in sequence from the prior slot. * We need to check the full history in the buffer at each poll to detect resets, which opens up a race between the read of the "next slot" (which is absent) and the full history retrieval (when it can now be present!). Detect that. * A null termination bug in the current output slot got fixed. Broadly: this was a huge bear to make work. It sounds like this should be a simple protocol, but it's not in practice. Also: clean up the error reporting in the script so it can handle new PCI IDs being added, and reports permissions failures on the required sysfs file as a human-readable error. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2020-12-16 21:47:02 +01:00
if (len == 0) {
return;
}
#ifdef CONFIG_ADSP_TRACE_SIMCALL
register int a2 __asm__("a2") = 4; /* SYS_write */
register int a3 __asm__("a3") = 1; /* fd 1 == stdout */
register int a4 __asm__("a4") = (int)str;
register int a5 __asm__("a5") = len;
__asm__ volatile("simcall" : "+r"(a2), "+r"(a3) : "r"(a4), "r"(a5) : "memory");
#endif
k_spinlock_key_t key = k_spin_lock(&trace_lock);
sys_winstream_write(winstream, str, len);
k_spin_unlock(&trace_lock, key);
}
int arch_printk_char_out(int c)
{
int8_t s = c;
winstream_console_trace_out(&s, 1);
return 0;
}
#if defined(CONFIG_STDOUT_CONSOLE)
extern void __stdout_hook_install(int (*hook)(int));
#else
#define __stdout_hook_install(x) \
do {/* nothing */ \
} while ((0))
#endif
#if defined(CONFIG_PRINTK)
extern void __printk_hook_install(int (*fn)(int));
#else
#define __printk_hook_install(x) \
do {/* nothing */ \
} while ((0))
#endif
static void winstream_console_hook_install(void)
{
__stdout_hook_install(arch_printk_char_out);
__printk_hook_install(arch_printk_char_out);
}
init: remove the need for a dummy device pointer in SYS_INIT functions The init infrastructure, found in `init.h`, is currently used by: - `SYS_INIT`: to call functions before `main` - `DEVICE_*`: to initialize devices They are all sorted according to an initialization level + a priority. `SYS_INIT` calls are really orthogonal to devices, however, the required function signature requires a `const struct device *dev` as a first argument. The only reason for that is because the same init machinery is used by devices, so we have something like: ```c struct init_entry { int (*init)(const struct device *dev); /* only set by DEVICE_*, otherwise NULL */ const struct device *dev; } ``` As a result, we end up with such weird/ugly pattern: ```c static int my_init(const struct device *dev) { /* always NULL! add ARG_UNUSED to avoid compiler warning */ ARG_UNUSED(dev); ... } ``` This is really a result of poor internals isolation. This patch proposes a to make init entries more flexible so that they can accept sytem initialization calls like this: ```c static int my_init(void) { ... } ``` This is achieved using a union: ```c union init_function { /* for SYS_INIT, used when init_entry.dev == NULL */ int (*sys)(void); /* for DEVICE*, used when init_entry.dev != NULL */ int (*dev)(const struct device *dev); }; struct init_entry { /* stores init function (either for SYS_INIT or DEVICE*) union init_function init_fn; /* stores device pointer for DEVICE*, NULL for SYS_INIT. Allows * to know which union entry to call. */ const struct device *dev; } ``` This solution **does not increase ROM usage**, and allows to offer clean public APIs for both SYS_INIT and DEVICE*. Note that however, init machinery keeps a coupling with devices. **NOTE**: This is a breaking change! All `SYS_INIT` functions will need to be converted to the new signature. See the script offered in the following commit. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> init: convert SYS_INIT functions to the new signature Conversion scripted using scripts/utils/migrate_sys_init.py. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> manifest: update projects for SYS_INIT changes Update modules with updated SYS_INIT calls: - hal_ti - lvgl - sof - TraceRecorderSource Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> tests: devicetree: devices: adjust test Adjust test according to the recently introduced SYS_INIT infrastructure. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> tests: kernel: threads: adjust SYS_INIT call Adjust to the new signature: int (*init_fn)(void); Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-10-19 09:33:44 +02:00
static int winstream_console_init(void)
{
const struct device *dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
if (!device_is_ready(dev)) {
return -ENODEV;
}
const struct mem_win_config *config = dev->config;
void *buf =
sys_cache_uncached_ptr_get((__sparse_force void __sparse_cache *)config->mem_base);
winstream = sys_winstream_init(buf, config->size);
winstream_console_hook_install();
return 0;
}
SYS_INIT(winstream_console_init, PRE_KERNEL_1, CONFIG_CONSOLE_INIT_PRIORITY);