optee 中可以有自己的console来打印log,每个厂商必须实现自己的console driver,这里以D02为例
其code在core/arch/arm/plat-d02/main.c 中,所有注册给optee的console 必须调用register_serial_console,这种机制和kernel中的类似
static struct hi16xx_uart_data console_data __early_bss;
void console_init(void)
{
hi16xx_uart_init(&console_data, CONSOLE_UART_BASE,
CONSOLE_UART_CLK_IN_HZ, CONSOLE_BAUDRATE);
register_serial_console(&console_data.chip);
}
在console_init中首先通过hi16xx_uart_init来初始化console_data.chip,最后调用register_serial_console来注册console
void hi16xx_uart_init(struct hi16xx_uart_data *pd, paddr_t base,
uint32_t uart_clk, uint32_t baud_rate)
{
uint16_t freq_div = uart_clk / (16 * baud_rate);
pd->base.pa = base;
//给chip.ops 赋值,这个最重要。最后的对串口的读写最后都是通过chip.ops是实现。
pd->chip.ops = &hi16xx_uart_ops;
//下面这些都有注释
/* Enable (and clear) FIFOs */
write32(UART_FCR_FIFO_EN, base + UART_FCR);
/* Enable access to _DLL and _DLH */
write32(UART_LCR_DLAB, base + UART_LCR);
/* Calculate and set UART_DLL */
write32(freq_div & 0xFF, base + UART_DLL);
/* Calculate and set UART_DLH */
write32((freq_div >> 8) & 0xFF, base + UART_DLH);
/* Clear _DLL/_DLH access bit, set data size (8 bits), parity etc. */
write32(UART_LCR_DLS8, base + UART_LCR);
//禁止中断,可见optee os中的console是通过poll的方式打印的
/* Disable interrupt mode */
write32(0, base + UART_IEL);
//通过写寄存器的方式flush 串口
hi16xx_uart_flush(&pd->chip);
}
static const struct serial_ops hi16xx_uart_ops = {
.flush = hi16xx_uart_flush,
.getchar = hi16xx_uart_getchar,
.have_rx_data = hi16xx_uart_have_rx_data,
.putc = hi16xx_uart_putc,
};
这里以常用的putc为例
static void hi16xx_uart_putc(struct serial_chip *chip, int ch)
{
vaddr_t base = chip_to_base(chip);
/* Wait until TX FIFO is empty */
while (!(read32(base + UART_USR) & UART_USR_TFE_BIT))
;
/* Put character into TX FIFO */
write32(ch & 0xFF, base + UART_THR);
}
原来就是通过写寄存器的方式输出字符。
回到console_init 中调用register_serial_console 来注册console
static struct serial_chip *serial_console __early_bss;
void __weak console_putc(int ch)
{
if (!serial_console)
return;
if (ch == '\n')
serial_console->ops->putc(serial_console, '\r');
serial_console->ops->putc(serial_console, ch);
}
void __weak console_flush(void)
{
if (!serial_console)
return;
serial_console->ops->flush(serial_console);
}
void register_serial_console(struct serial_chip *chip)
{
serial_console = chip;
}
原来就是给静态变量serial_console 赋值,从register_serial_console 中也可以看出optee中的console只能有一个,如果register_serial_console 被调用多次的话,以最后一个的console为准
这样就可以在code中通过console_putc来打印log
但实际情况是我们并不能直接使用console_putc,而是外包了一层API
if (tzc_id != TZC400_COMPONENT_ID) {
EMSG("TZC : Wrong device ID (0x%x).\n", tzc_id);
panic();
}
例如错误信息就用EMSG
#if (TRACE_LEVEL <= 0)
#define MSG(...) (void)0
#else
#define MSG(...) trace_printf_helper(0, false, __VA_ARGS__)
#endif
/* Formatted trace tagged with TRACE_ERROR level */
#if (TRACE_LEVEL < TRACE_ERROR)
#define EMSG(...) (void)0
#else
#define EMSG(...) trace_printf_helper(TRACE_ERROR, true, __VA_ARGS__)
#endif
/* Formatted trace tagged with TRACE_INFO level */
#if (TRACE_LEVEL < TRACE_INFO)
#define IMSG(...) (void)0
#else
#define IMSG(...) trace_printf_helper(TRACE_INFO, true, __VA_ARGS__)
#endif
其他的级别如上,就是可以控制打印的级别
如果要改变打印的级别的话
#ifndef TRACE_LEVEL
#define TRACE_LEVEL TRACE_MAX
#endif
可以修改TRACE_LEVEL,这个值默认是最大,也就是一行log都不打印
这些最终都会调用trace_printf_helper
#define trace_printf_helper(level, level_ok, ...) \
trace_printf(__func__, __LINE__, (level), (level_ok), \
__VA_ARGS__)
trace_printf在通过snprintk格式话后最后调用trace_ext_puts显示log
void trace_ext_puts(const char *str)
{
uint32_t itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
bool mmu_enabled = cpu_mmu_enabled();
bool was_contended = false;
const char *p;
if (mmu_enabled && !cpu_spin_trylock(&puts_lock)) {
was_contended = true;
cpu_spin_lock(&puts_lock);
}
console_flush();
if (was_contended)
console_putc('*');
for (p = str; *p; p++)
console_putc(*p);
console_flush();
if (mmu_enabled)
cpu_spin_unlock(&puts_lock);
thread_unmask_exceptions(itr_status);
}
可见在trace_ext_puts 中还是用我们之前的console_putc来打印
其code在core/arch/arm/plat-d02/main.c 中,所有注册给optee的console 必须调用register_serial_console,这种机制和kernel中的类似
static struct hi16xx_uart_data console_data __early_bss;
void console_init(void)
{
hi16xx_uart_init(&console_data, CONSOLE_UART_BASE,
CONSOLE_UART_CLK_IN_HZ, CONSOLE_BAUDRATE);
register_serial_console(&console_data.chip);
}
在console_init中首先通过hi16xx_uart_init来初始化console_data.chip,最后调用register_serial_console来注册console
void hi16xx_uart_init(struct hi16xx_uart_data *pd, paddr_t base,
uint32_t uart_clk, uint32_t baud_rate)
{
uint16_t freq_div = uart_clk / (16 * baud_rate);
pd->base.pa = base;
//给chip.ops 赋值,这个最重要。最后的对串口的读写最后都是通过chip.ops是实现。
pd->chip.ops = &hi16xx_uart_ops;
//下面这些都有注释
/* Enable (and clear) FIFOs */
write32(UART_FCR_FIFO_EN, base + UART_FCR);
/* Enable access to _DLL and _DLH */
write32(UART_LCR_DLAB, base + UART_LCR);
/* Calculate and set UART_DLL */
write32(freq_div & 0xFF, base + UART_DLL);
/* Calculate and set UART_DLH */
write32((freq_div >> 8) & 0xFF, base + UART_DLH);
/* Clear _DLL/_DLH access bit, set data size (8 bits), parity etc. */
write32(UART_LCR_DLS8, base + UART_LCR);
//禁止中断,可见optee os中的console是通过poll的方式打印的
/* Disable interrupt mode */
write32(0, base + UART_IEL);
//通过写寄存器的方式flush 串口
hi16xx_uart_flush(&pd->chip);
}
static const struct serial_ops hi16xx_uart_ops = {
.flush = hi16xx_uart_flush,
.getchar = hi16xx_uart_getchar,
.have_rx_data = hi16xx_uart_have_rx_data,
.putc = hi16xx_uart_putc,
};
这里以常用的putc为例
static void hi16xx_uart_putc(struct serial_chip *chip, int ch)
{
vaddr_t base = chip_to_base(chip);
/* Wait until TX FIFO is empty */
while (!(read32(base + UART_USR) & UART_USR_TFE_BIT))
;
/* Put character into TX FIFO */
write32(ch & 0xFF, base + UART_THR);
}
原来就是通过写寄存器的方式输出字符。
回到console_init 中调用register_serial_console 来注册console
static struct serial_chip *serial_console __early_bss;
void __weak console_putc(int ch)
{
if (!serial_console)
return;
if (ch == '\n')
serial_console->ops->putc(serial_console, '\r');
serial_console->ops->putc(serial_console, ch);
}
void __weak console_flush(void)
{
if (!serial_console)
return;
serial_console->ops->flush(serial_console);
}
void register_serial_console(struct serial_chip *chip)
{
serial_console = chip;
}
原来就是给静态变量serial_console 赋值,从register_serial_console 中也可以看出optee中的console只能有一个,如果register_serial_console 被调用多次的话,以最后一个的console为准
这样就可以在code中通过console_putc来打印log
但实际情况是我们并不能直接使用console_putc,而是外包了一层API
if (tzc_id != TZC400_COMPONENT_ID) {
EMSG("TZC : Wrong device ID (0x%x).\n", tzc_id);
panic();
}
例如错误信息就用EMSG
#if (TRACE_LEVEL <= 0)
#define MSG(...) (void)0
#else
#define MSG(...) trace_printf_helper(0, false, __VA_ARGS__)
#endif
/* Formatted trace tagged with TRACE_ERROR level */
#if (TRACE_LEVEL < TRACE_ERROR)
#define EMSG(...) (void)0
#else
#define EMSG(...) trace_printf_helper(TRACE_ERROR, true, __VA_ARGS__)
#endif
/* Formatted trace tagged with TRACE_INFO level */
#if (TRACE_LEVEL < TRACE_INFO)
#define IMSG(...) (void)0
#else
#define IMSG(...) trace_printf_helper(TRACE_INFO, true, __VA_ARGS__)
#endif
其他的级别如上,就是可以控制打印的级别
如果要改变打印的级别的话
#ifndef TRACE_LEVEL
#define TRACE_LEVEL TRACE_MAX
#endif
可以修改TRACE_LEVEL,这个值默认是最大,也就是一行log都不打印
这些最终都会调用trace_printf_helper
#define trace_printf_helper(level, level_ok, ...) \
trace_printf(__func__, __LINE__, (level), (level_ok), \
__VA_ARGS__)
trace_printf在通过snprintk格式话后最后调用trace_ext_puts显示log
void trace_ext_puts(const char *str)
{
uint32_t itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
bool mmu_enabled = cpu_mmu_enabled();
bool was_contended = false;
const char *p;
if (mmu_enabled && !cpu_spin_trylock(&puts_lock)) {
was_contended = true;
cpu_spin_lock(&puts_lock);
}
console_flush();
if (was_contended)
console_putc('*');
for (p = str; *p; p++)
console_putc(*p);
console_flush();
if (mmu_enabled)
cpu_spin_unlock(&puts_lock);
thread_unmask_exceptions(itr_status);
}
可见在trace_ext_puts 中还是用我们之前的console_putc来打印