diff --git a/Makefile b/Makefile index 9dc8a42..5aa8db9 100644 --- a/Makefile +++ b/Makefile @@ -416,12 +416,6 @@ $(error USE_COHERENT_MEM cannot be enabled with HW_ASSISTED_COHERENCY) endif -ifneq ($(MULTI_CONSOLE_API), 0) - ifeq (${ARCH},aarch32) - $(error "Error: MULTI_CONSOLE_API is not supported for AArch32") - endif -endif - #For now, BL2_IN_XIP_MEM is only supported when BL2_AT_EL3 is 1. ifeq ($(BL2_AT_EL3)-$(BL2_IN_XIP_MEM),0-1) $(error "BL2_IN_XIP_MEM is only supported when BL2_AT_EL3 is enabled") diff --git a/drivers/console/aarch32/console.S b/drivers/console/aarch32/console.S index a3c6546..f909609 100644 --- a/drivers/console/aarch32/console.S +++ b/drivers/console/aarch32/console.S @@ -3,104 +3,9 @@ * * SPDX-License-Identifier: BSD-3-Clause */ -#include - .globl console_init - .globl console_uninit - .globl console_putc - .globl console_getc - .globl console_flush - - /* - * The console base is in the data section and not in .bss - * even though it is zero-init. In particular, this allows - * the console functions to start using this variable before - * the runtime memory is initialized for images which do not - * need to copy the .data section from ROM to RAM. - */ -.section .data.console_base ; .align 2 - console_base: .word 0x0 - - /* ----------------------------------------------- - * int console_init(uintptr_t base_addr, - * unsigned int uart_clk, unsigned int baud_rate) - * Function to initialize the console without a - * C Runtime to print debug information. It saves - * the console base to the data section. - * In: r0 - console base address - * r1 - Uart clock in Hz - * r2 - Baud rate - * out: return 1 on success else 0 on error - * Clobber list : r1 - r3 - * ----------------------------------------------- - */ -func console_init - /* Check the input base address */ - cmp r0, #0 - beq init_fail - ldr r3, =console_base - str r0, [r3] - b console_core_init -init_fail: - bx lr -endfunc console_init - - /* ----------------------------------------------- - * void console_uninit(void) - * Function to finish the use of console driver. - * It sets the console_base as NULL so that any - * further invocation of `console_putc` or - * `console_getc` APIs would return error. - * ----------------------------------------------- - */ -func console_uninit - mov r0, #0 - ldr r3, =console_base - str r0, [r3] - bx lr -endfunc console_uninit - - /* --------------------------------------------- - * int console_putc(int c) - * Function to output a character over the - * console. It returns the character printed on - * success or -1 on error. - * In : r0 - character to be printed - * Out : return -1 on error else return character. - * Clobber list : r1, r2 - * --------------------------------------------- - */ -func console_putc - ldr r2, =console_base - ldr r1, [r2] - b console_core_putc -endfunc console_putc - - /* --------------------------------------------- - * int console_getc(void) - * Function to get a character from the console. - * It returns the character grabbed on success - * or -1 on error. - * Clobber list : r0, r1 - * --------------------------------------------- - */ -func console_getc - ldr r1, =console_base - ldr r0, [r1] - b console_core_getc -endfunc console_getc - - /* --------------------------------------------- - * int console_flush(void) - * Function to force a write of all buffered - * data that hasn't been output. It returns 0 - * upon successful completion, otherwise it - * returns -1. - * Clobber list : r0, r1 - * --------------------------------------------- - */ -func console_flush - ldr r1, =console_base - ldr r0, [r1] - b console_core_flush -endfunc console_flush + #if MULTI_CONSOLE_API + #include "multi_console.S" + #else + #include "deprecated_console.S" + #endif diff --git a/drivers/console/aarch32/deprecated_console.S b/drivers/console/aarch32/deprecated_console.S new file mode 100644 index 0000000..f7e3c4f --- /dev/null +++ b/drivers/console/aarch32/deprecated_console.S @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include + +/* + * This is the common console core code for the deprecated single-console API. + * New platforms should set MULTI_CONSOLE_API=1 and not use this file. + */ +#warning "Using deprecated console implementation. Please migrate to MULTI_CONSOLE_API" + + .globl console_init + .globl console_uninit + .globl console_putc + .globl console_getc + .globl console_flush + + /* + * The console base is in the data section and not in .bss + * even though it is zero-init. In particular, this allows + * the console functions to start using this variable before + * the runtime memory is initialized for images which do not + * need to copy the .data section from ROM to RAM. + */ +.section .data.console_base ; .align 2 + console_base: .word 0x0 + + /* ----------------------------------------------- + * int console_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. It saves + * the console base to the data section. + * In: r0 - console base address + * r1 - Uart clock in Hz + * r2 - Baud rate + * out: return 1 on success else 0 on error + * Clobber list : r1 - r3 + * ----------------------------------------------- + */ +func console_init + /* Check the input base address */ + cmp r0, #0 + beq init_fail + ldr r3, =console_base + str r0, [r3] + b console_core_init +init_fail: + bx lr +endfunc console_init + + /* ----------------------------------------------- + * void console_uninit(void) + * Function to finish the use of console driver. + * It sets the console_base as NULL so that any + * further invocation of `console_putc` or + * `console_getc` APIs would return error. + * ----------------------------------------------- + */ +func console_uninit + mov r0, #0 + ldr r3, =console_base + str r0, [r3] + bx lr +endfunc console_uninit + + /* --------------------------------------------- + * int console_putc(int c) + * Function to output a character over the + * console. It returns the character printed on + * success or -1 on error. + * In : r0 - character to be printed + * Out : return -1 on error else return character. + * Clobber list : r1, r2 + * --------------------------------------------- + */ +func console_putc + ldr r2, =console_base + ldr r1, [r2] + b console_core_putc +endfunc console_putc + + /* --------------------------------------------- + * int console_getc(void) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * Clobber list : r0, r1 + * --------------------------------------------- + */ +func console_getc + ldr r1, =console_base + ldr r0, [r1] + b console_core_getc +endfunc console_getc + + /* --------------------------------------------- + * int console_flush(void) + * Function to force a write of all buffered + * data that hasn't been output. It returns 0 + * upon successful completion, otherwise it + * returns -1. + * Clobber list : r0, r1 + * --------------------------------------------- + */ +func console_flush + ldr r1, =console_base + ldr r0, [r1] + b console_core_flush +endfunc console_flush diff --git a/drivers/console/aarch32/multi_console.S b/drivers/console/aarch32/multi_console.S new file mode 100644 index 0000000..e23b20e --- /dev/null +++ b/drivers/console/aarch32/multi_console.S @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + + .globl console_register + .globl console_unregister + .globl console_is_registered + .globl console_set_scope + .globl console_switch_state + .globl console_putc + .globl console_getc + .globl console_flush + + /* + * The console list pointer is in the data section and not in + * .bss even though it is zero-init. In particular, this allows + * the console functions to start using this variable before + * the runtime memory is initialized for images which do not + * need to copy the .data section from ROM to RAM. + */ +.section .data.console_list ; .align 2 + console_list: .word 0x0 +.section .data.console_state ; .align 0 + console_state: .byte CONSOLE_FLAG_BOOT + + /* ----------------------------------------------- + * int console_register(console_t *console) + * Function to insert a new console structure into + * the console list. Should usually be called by + * console__register implementations. The + * data structure passed will be taken over by the + * console framework and *MUST* be allocated in + * persistent memory (e.g. the data section). + * In : r0 - address of console_t structure + * Out: r0 - Always 1 (for easier tail calling) + * Clobber list: r0, r1 + * ----------------------------------------------- + */ +func console_register + push {r6, lr} +#if ENABLE_ASSERTIONS + /* Assert that r0 isn't a NULL pointer */ + cmp r0, #0 + ASM_ASSERT(ne) + /* Assert that the struct isn't in the stack */ + ldr r1, =__STACKS_START__ + cmp r0, r1 + blo not_on_stack + ldr r1, =__STACKS_END__ + cmp r0, r1 + ASM_ASSERT(hs) +not_on_stack: + /* Assert that this struct isn't in the list */ + mov r1, r0 /* Preserve r0 and lr */ + bl console_is_registered + cmp r0, #0 + ASM_ASSERT(eq) + mov r0, r1 +#endif /* ENABLE_ASSERTIONS */ + ldr r6, =console_list + ldr r1, [r6] /* R1 = first struct in list */ + str r0, [r6] /* list head = new console */ + str r1, [r0, #CONSOLE_T_NEXT] /* new console next ptr = R1 */ + mov r0, #1 + pop {r6, pc} +endfunc console_register + + /* ----------------------------------------------- + * int console_unregister(console_t *console) + * Function to find a specific console in the list + * of currently active consoles and remove it. + * In: r0 - address of console_t struct to remove + * Out: r0 - removed address, or NULL if not found + * Clobber list: r0, r1 + * ----------------------------------------------- + */ +func console_unregister +#if ENABLE_ASSERTIONS + /* Assert that r0 isn't a NULL pointer */ + cmp r0, #0 + ASM_ASSERT(ne) +#endif /* ENABLE_ASSERTIONS */ + push {r6} + ldr r6, =console_list /* R6 = ptr to first struct */ + ldr r1, [r6] /* R1 = first struct */ + +unregister_loop: + cmp r1, #0 + beq unregister_not_found + cmp r0, r1 + beq unregister_found + ldr r6, [r6] /* R6 = next ptr of struct */ + ldr r1, [r6] /* R1 = next struct */ + b unregister_loop + +unregister_found: + ldr r1, [r1] /* R1 = next struct */ + str r1, [r6] /* prev->next = cur->next */ + pop {r6} + bx lr + +unregister_not_found: + mov r0, #0 /* return NULL if not found */ + pop {r6} + bx lr +endfunc console_unregister + + /* ----------------------------------------------- + * int console_is_registered(console_t *console) + * Function to detect if a specific console is + * registered or not. + * In: r0 - address of console_t struct to remove + * Out: r0 - 1 if it is registered, 0 if not. + * Clobber list: r0 + * ----------------------------------------------- + */ +func console_is_registered +#if ENABLE_ASSERTIONS + /* Assert that r0 isn't a NULL pointer */ + cmp r0, #0 + ASM_ASSERT(ne) +#endif /* ENABLE_ASSERTIONS */ + push {r6} + ldr r6, =console_list + ldr r6, [r6] /* R6 = first console struct */ +check_registered_loop: + cmp r6, #0 /* Check if end of list */ + beq console_not_registered + cmp r0, r6 /* Check if the pointers are different */ + beq console_registered + ldr r6, [r6, #CONSOLE_T_NEXT] /* Get pointer to next struct */ + b check_registered_loop +console_not_registered: + mov r0, #0 + pop {r6} + bx lr +console_registered: + mov r0, #1 + pop {r6} + bx lr +endfunc console_is_registered + + /* ----------------------------------------------- + * void console_switch_state(unsigned int new_state) + * Function to switch the current console state. + * The console state determines which of the + * registered consoles are actually used at a time. + * In : r0 - global console state to move to + * Clobber list: r0, r1 + * ----------------------------------------------- + */ +func console_switch_state + ldr r1, =console_state + strb r0, [r1] + bx lr +endfunc console_switch_state + + /* ----------------------------------------------- + * void console_set_scope(console_t *console, + * unsigned int scope) + * Function to update the states that a given console + * may be active in. + * In : r0 - pointer to console_t struct + * : r1 - new active state mask + * Clobber list: r0, r1, r2 + * ----------------------------------------------- + */ +func console_set_scope +#if ENABLE_ASSERTIONS + ands r2, r1, #~CONSOLE_FLAG_SCOPE_MASK + ASM_ASSERT(eq) +#endif /* ENABLE_ASSERTIONS */ + ldr r2, [r0, #CONSOLE_T_FLAGS] + and r2, r2, #~CONSOLE_FLAG_SCOPE_MASK + orr r2, r2, r1 + str r2, [r0, #CONSOLE_T_FLAGS] + bx lr +endfunc console_set_scope + + /* --------------------------------------------- + * int console_putc(int c) + * Function to output a character. Calls all + * active console's putc() handlers in succession. + * In : r0 - character to be printed + * Out: r0 - printed character on success, or < 0 + if at least one console had an error + * Clobber list : r0, r1, r2 + * --------------------------------------------- + */ +func console_putc + push {r4-r6, lr} + mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */ + mov r4, r0 /* R4 = character to print */ + ldr r6, =console_list + ldr r6, [r6] /* R6 = first console struct */ + +putc_loop: + cmp r6, #0 + beq putc_done + ldr r1, =console_state + ldrb r1, [r1] + ldr r2, [r6, #CONSOLE_T_FLAGS] + tst r1, r2 + beq putc_continue + ldr r2, [r6, #CONSOLE_T_PUTC] + cmp r2, #0 + beq putc_continue + mov r0, r4 + mov r1, r6 + blx r2 + cmp r5, #ERROR_NO_VALID_CONSOLE /* update R5 if it's NOVALID */ + cmpne r0, #0 /* else update it if R0 < 0 */ + movlt r5, r0 +putc_continue: + ldr r6, [r6] /* R6 = next struct */ + b putc_loop + +putc_done: + mov r0, r5 + pop {r4-r6, pc} +endfunc console_putc + + /* --------------------------------------------- + * int console_getc(void) + * Function to get a character from any console. + * Keeps looping through all consoles' getc() + * handlers until one of them returns a + * character, then stops iterating and returns + * that character to the caller. Will stop looping + * if all active consoles report real errors + * (other than just not having a char available). + * Out : r0 - read character, or < 0 on error + * Clobber list : r0, r1 + * --------------------------------------------- + */ +func console_getc + push {r5-r6, lr} +getc_try_again: + mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */ + ldr r6, =console_list + ldr r6, [r6] /* R6 = first console struct */ + cmp r6, #0 + bne getc_loop + mov r0, r5 /* If no consoles registered */ + pop {r5-r6, pc} /* return immediately. */ + +getc_loop: + ldr r0, =console_state + ldrb r0, [r0] + ldr r1, [r6, #CONSOLE_T_FLAGS] + tst r0, r1 + beq getc_continue + ldr r1, [r6, #CONSOLE_T_GETC] + cmp r1, #0 + beq getc_continue + mov r0, r6 + blx r1 + cmp r0, #0 /* if R0 >= 0: return */ + bge getc_found + cmp r5, #ERROR_NO_PENDING_CHAR /* may update R5 (NOCHAR has */ + movne r5, r0 /* precedence vs real errors) */ +getc_continue: + ldr r6, [r6] /* R6 = next struct */ + cmp r6, #0 + bne getc_loop + cmp r5, #ERROR_NO_PENDING_CHAR /* Keep scanning if at least */ + beq getc_try_again /* one console returns NOCHAR */ + mov r0, r5 + +getc_found: + pop {r5-r6, pc} +endfunc console_getc + + /* --------------------------------------------- + * int console_flush(void) + * Function to force a write of all buffered + * data that hasn't been output. Calls all + * console's flush() handlers in succession. + * Out: r0 - 0 on success, < 0 if at least one error + * Clobber list : r0, r1, r2 + * --------------------------------------------- + */ +func console_flush + push {r5-r6, lr} + mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */ + ldr r6, =console_list + ldr r6, [r6] /* R6 = first console struct */ + +flush_loop: + cmp r6, #0 + beq flush_done + ldr r1, =console_state + ldrb r1, [r1] + ldr r2, [r6, #CONSOLE_T_FLAGS] + tst r1, r2 + beq flush_continue + ldr r1, [r6, #CONSOLE_T_FLUSH] + cmp r1, #0 + beq flush_continue + mov r0, r6 + blx r1 + cmp r5, #ERROR_NO_VALID_CONSOLE /* update R5 if it's NOVALID */ + cmpne r0, #0 /* else update it if R0 < 0 */ + movlt r5, r0 +flush_continue: + ldr r6, [r6] /* R6 = next struct */ + b flush_loop + +flush_done: + mov r0, r5 + pop {r5-r6, pc} +endfunc console_flush