diff --git a/src/build/img_defs.mk b/src/build/img_defs.mk index c0540f6d8..035b4afc3 100644 --- a/src/build/img_defs.mk +++ b/src/build/img_defs.mk @@ -520,6 +520,10 @@ ifdef SBE_CONSOLE_SUPPORT GCC-DEFS += -DSBE_CONSOLE_SUPPORT endif +ifdef SBE_IPL_STATUS_LPC_SUPPORT +GCC-DEFS += -DSBE_IPL_STATUS_LPC_SUPPORT +endif + DEFS += $(GCC-DEFS) ########################################################################### diff --git a/src/build/power_defs.mk b/src/build/power_defs.mk index 6d368ec3b..2863f6eb0 100644 --- a/src/build/power_defs.mk +++ b/src/build/power_defs.mk @@ -36,6 +36,7 @@ HOST_INTERFACE_AVAILABLE = 1 PERIODIC_IO_TOGGLE_SUPPORTED = 1 SBE_CONSOLE_SUPPORT = 1 +SBE_IPL_STATUS_LPC_SUPPORT = 1 export SBE_S0_SUPPORT = 1 export PIBMEM_REPAIR_SCOM_P9 = 1 diff --git a/src/hwpf/plat_trace.H b/src/hwpf/plat_trace.H index 8a9344c67..9a323a9da 100644 --- a/src/hwpf/plat_trace.H +++ b/src/hwpf/plat_trace.H @@ -45,6 +45,10 @@ extern "C" #include "trac_interface.h" } +#ifdef SBE_CONSOLE_SUPPORT +#include "printf.H" +#endif + // Why not a #define, why is this in the fapi2 namespace? // To prevent problems with Cronus and the fapi1 definitions. namespace fapi2 @@ -52,9 +56,16 @@ namespace fapi2 static const uint32_t MAX_ECMD_STRING_LEN = 64; }; +#if defined(SBE_CONSOLE_SUPPORT) && defined(ENABLE_CONSOLE_TRACE_IN_FILE) +#define FAPI_TRACE(_id_, _fmt_, _args_...) { \ + printf(_fmt_, ##_args_); \ + printf("\n\r"); \ +} +#else // Information traces (go into fast trace buffer that can wrap often) #define FAPI_TRACE(_id_, _fmt_, _args_...) \ PK_TRACE(_fmt_, ##_args_); +#endif /* The following is a desirous trace entry but the second line has a diff --git a/src/import/chips/p9/procedures/hwp/perv/p9_sbe_lpc_init.C b/src/import/chips/p9/procedures/hwp/perv/p9_sbe_lpc_init.C index 9d73e6891..d8e38bf46 100644 --- a/src/import/chips/p9/procedures/hwp/perv/p9_sbe_lpc_init.C +++ b/src/import/chips/p9/procedures/hwp/perv/p9_sbe_lpc_init.C @@ -7,6 +7,7 @@ /* */ /* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] International Business Machines Corp. */ +/* COPYRIGHT 2020 Raptor Engineering, LLC */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -35,7 +36,6 @@ // *HWP Consumed by : SBE //------------------------------------------------------------------------------ - //## auto_generated #include "p9_sbe_lpc_init.H" @@ -46,6 +46,9 @@ #include "p9_lpc_utils.H" +#include "sbeglobals.H" +#include "sbeConsole.H" + static fapi2::ReturnCode switch_lpc_clock_mux( const fapi2::Target& i_target_chip, bool use_nest_clock) @@ -198,3 +201,38 @@ fapi_try_exit: return fapi2::current_err; } + +fapi2::ReturnCode p9_sbe_lpc_clear_errors( + const fapi2::Target& i_target_chip) +{ + fapi2::buffer l_data32; + FAPI_DBG("p9_sbe_lpc_clear_errors: Entering ..."); + + FAPI_TRY(lpc_read(i_target_chip, LPCM_OPB_MASTER_STATUS_REG, l_data32), + "Error reading OPB master status register"); + if (l_data32 & LPCM_OPB_MASTER_STATUS_ERROR_BITS) + { + SBE_MSG_CONSOLE("Errors detected on LPC bus during SBE execution"); + SBE_MSG_CONSOLE("Clearing errors to allow HBBL start"); + + // Clear any stale LPC bus errors + l_data32 = LPCM_OPB_MASTER_STATUS_ERROR_BITS; + FAPI_TRY(lpc_write(i_target_chip, LPCM_OPB_MASTER_ACTUAL_STATUS_REG, l_data32), "Error clearing LPC error actual status register"); + l_data32 = LPCM_OPB_MASTER_STATUS_ERROR_BITS; + FAPI_TRY(lpc_write(i_target_chip, LPCM_OPB_MASTER_STATUS_REG, l_data32), "Error clearing LPC error status register"); + l_data32 = LPC_HC_IRQ_BASE_IRQS; + FAPI_TRY(lpc_write(i_target_chip, LPC_REG_OPB_BASE + LPC_HC_IRQSTAT, l_data32), "Error clearing LPC error status IRQ register"); + } + + // Verify errors are cleared + FAPI_TRY(lpc_read(i_target_chip, LPCM_OPB_MASTER_STATUS_REG, l_data32), "Error reading OPB master status register"); + if (l_data32 & LPCM_OPB_MASTER_STATUS_ERROR_BITS) + { + SBE_MSG_CONSOLE("LPC error register still reporting failure! HBBL is likely to abort."); + } + + FAPI_DBG("p9_sbe_lpc_clear_errors: Exiting ..."); + +fapi_try_exit: + return fapi2::current_err; +} diff --git a/src/import/chips/p9/procedures/hwp/perv/p9_sbe_lpc_init.H b/src/import/chips/p9/procedures/hwp/perv/p9_sbe_lpc_init.H index 913017264..c869f7501 100644 --- a/src/import/chips/p9/procedures/hwp/perv/p9_sbe_lpc_init.H +++ b/src/import/chips/p9/procedures/hwp/perv/p9_sbe_lpc_init.H @@ -7,6 +7,7 @@ /* */ /* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] International Business Machines Corp. */ +/* COPYRIGHT 2020 Raptor Engineering, LLC */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -55,6 +56,7 @@ extern "C" const uint64_t LPC_LRESET_OUT = 22; const uint32_t LPC_LRESET_DELAY_NS = 200000; const uint32_t LPCM_OPB_MASTER_STATUS_REG = 0xC0010000; + const uint32_t LPCM_OPB_MASTER_ACTUAL_STATUS_REG = 0xC001004C; const uint32_t LPCM_OPB_MASTER_STATUS_ERROR_BITS = 0x20000FC3; const uint32_t LPCM_OPB_MASTER_CONTROL_REG = 0xC0010008; const uint32_t LPCM_OPB_MASTER_CONTROL_REG_TIMEOUT_ENABLE = 2; @@ -63,8 +65,29 @@ extern "C" const uint32_t LPCM_LPC_MASTER_TIMEOUT_REG = 0xC001202C; const uint32_t LPCM_LPC_MASTER_TIMEOUT_VALUE = 0xFE000000; const uint32_t CPLT_CONF1_TC_LP_RESET = 12; + const uint32_t LPC_REG_OPB_BASE = 0XC0012000; + const uint32_t LPC_HC_IRQSTAT = 0x38; + const uint32_t LPC_HC_IRQ_LRESET = 0x00000400; + const uint32_t LPC_HC_IRQ_SYNC_ABNORM_ERR = 0x00000080; + const uint32_t LPC_HC_IRQ_SYNC_NORESP_ERR = 0x00000040; + const uint32_t LPC_HC_IRQ_SYNC_NORM_ERR = 0x00000020; + const uint32_t LPC_HC_IRQ_SYNC_TIMEOUT_ERR = 0x00000010; + const uint32_t LPC_HC_IRQ_TARG_TAR_ERR = 0x00000008; + const uint32_t LPC_HC_IRQ_BM_TAR_ERR = 0x00000004; + const uint32_t LPC_HC_IRQ_BM0_REQ = 0x00000002; + const uint32_t LPC_HC_IRQ_BM1_REQ = 0x00000001; + const uint32_t LPC_HC_IRQ_BASE_IRQS = ( \ + LPC_HC_IRQ_LRESET | \ + LPC_HC_IRQ_SYNC_ABNORM_ERR | \ + LPC_HC_IRQ_SYNC_NORESP_ERR | \ + LPC_HC_IRQ_SYNC_NORM_ERR | \ + LPC_HC_IRQ_SYNC_TIMEOUT_ERR | \ + LPC_HC_IRQ_TARG_TAR_ERR | \ + LPC_HC_IRQ_BM_TAR_ERR); fapi2::ReturnCode p9_sbe_lpc_init(const fapi2::Target& i_target_chip); + fapi2::ReturnCode p9_sbe_lpc_clear_errors(const + fapi2::Target& i_target_chip); } #endif diff --git a/src/sbefw/app/power/ipl_table.C b/src/sbefw/app/power/ipl_table.C index 442c33ff8..7b3043567 100644 --- a/src/sbefw/app/power/ipl_table.C +++ b/src/sbefw/app/power/ipl_table.C @@ -109,6 +109,8 @@ #include "p9_fbc_utils.H" #include "sbeSecureMemRegionManager.H" +#include "sbeEarlyLPC.H" +#include "sbeIPLStatusLPC.H" #include "sbeConsole.H" #include "sbecmdflushnvdimm.H" @@ -179,6 +181,7 @@ using sbeIstepHwpCacheInitf_t = ReturnCode (*) // Wrapper function which will call HWP. ReturnCode istepWithProc( voidfuncptr_t i_hwp ); ReturnCode istepLpcInit( voidfuncptr_t i_hwp ); +ReturnCode istepLpcClearErrors( voidfuncptr_t i_hwp ); ReturnCode istepHwpTpSwitchGears( voidfuncptr_t i_hwp); ReturnCode istepAttrSetup( voidfuncptr_t i_hwp ); ReturnCode istepNoOp( voidfuncptr_t i_hwp ); @@ -359,6 +362,7 @@ static istepMap_t g_istep5PtrTbl[] { #ifdef SEEPROM_IMAGE ISTEP_MAP( istepLoadBootLoader, NULL ), + ISTEP_MAP( istepLpcClearErrors, p9_sbe_lpc_clear_errors ), ISTEP_MAP( istepStartInstruction, p9_sbe_instruct_start ), #endif }; @@ -393,12 +397,27 @@ ReturnCode istepWithProc( voidfuncptr_t i_hwp) } //---------------------------------------------------------------------------- +ReturnCode istepLpcClearErrors( voidfuncptr_t i_hwp) +{ + #define SBE_FUNC "istepLpcClearErrors " + + ReturnCode rc = FAPI2_RC_SUCCESS; + Target proc = plat_getChipTarget(); + assert( NULL != i_hwp ); + SBE_EXEC_HWP(rc, reinterpret_cast( i_hwp ), proc) + + #undef SBE_FUNC + + return rc; +} + ReturnCode istepLpcInit( voidfuncptr_t i_hwp) { ReturnCode rc = FAPI2_RC_SUCCESS; Target proc = plat_getChipTarget(); assert( NULL != i_hwp ); SBE_EXEC_HWP(rc, reinterpret_cast( i_hwp ), proc) + SBE_LPC_INIT; SBE_UART_INIT; SBE_MSG_CONSOLE( SBE_CONSOLE_WELCOME_MSG ); @@ -662,6 +681,7 @@ ReturnCode istepStartInstruction( voidfuncptr_t i_hwp) ReturnCode rc = FAPI2_RC_SUCCESS; SBE_MSG_CONSOLE("SBE starting hostboot"); + SBE_LPC_DISABLE; SBE_UART_DISABLE; rc = istepWithCore(i_hwp); if(rc == FAPI2_RC_SUCCESS) diff --git a/src/sbefw/app/power/sbecmdmemaccess.C b/src/sbefw/app/power/sbecmdmemaccess.C index b34637dca..76a097b09 100644 --- a/src/sbefw/app/power/sbecmdmemaccess.C +++ b/src/sbefw/app/power/sbecmdmemaccess.C @@ -250,7 +250,7 @@ uint32_t processPbaRequest(const sbeMemAccessReqMsgHdr_t &i_hdr, // by LCO_mode (LCO Mode for PBA-Put) if(i_hdr.isPbaLcoModeSet()) { - SBE_INFO(SBE_INFO "LCO Mode is set with Ex ChipletId[%d]", + SBE_INFO(SBE_FUNC "LCO Mode is set with Ex ChipletId[%d]", (i_hdr.coreChipletId)/2); //Derive the EX target from the input Core Chiplet Id //Core0/1 -> EX0, Core2/3 -> EX1, Core4/5 -> EX2, Core6/7 -> EX3 diff --git a/src/sbefw/core/corefiles.mk b/src/sbefw/core/corefiles.mk index b14d0b0fe..667a6f9d3 100644 --- a/src/sbefw/core/corefiles.mk +++ b/src/sbefw/core/corefiles.mk @@ -56,11 +56,16 @@ COREPIBMEM_OBJECTS = $(COREPIBMEM-C-SOURCES:.c=.o) $(COREPIBMEM-CPP-SOURCES:.C=. # seeprom objects CORESEEPROM-CPP-SOURCES = sbeSecureMemRegionManager.C +CORESEEPROM-CPP-SOURCES += sbeEarlyLPC.C ifeq ($(SBE_S0_SUPPORT), 1) CORESEEPROM-CPP-SOURCES += sbes0handler.C endif ifeq ($(SBE_CONSOLE_SUPPORT), 1) CORESEEPROM-CPP-SOURCES += sbeConsole.C +CORESEEPROM-CPP-SOURCES += printf.C +endif +ifeq ($(SBE_IPL_STATUS_LPC_SUPPORT), 1) +CORESEEPROM-CPP-SOURCES += sbeIPLStatusLPC.C endif CORESEEPROM-C-SOURCES = diff --git a/src/sbefw/core/ipl.C b/src/sbefw/core/ipl.C index 14dd31629..68ce34013 100644 --- a/src/sbefw/core/ipl.C +++ b/src/sbefw/core/ipl.C @@ -29,6 +29,7 @@ #include "ipl.H" #include "sbeConsole.H" +#include "sbeIPLStatusLPC.H" #include "sbeglobals.H" #include "p9n2_perv_scom_addresses.H" @@ -122,6 +123,7 @@ void sbeDoContinuousIpl() auto istepMap = &istepTableEntry->istepMinorArr[step-1]; if(istepMap->istepWrapper != NULL) { + SBE_PUT_ISTEP_LPC(istepTableEntry->istepMajorNum, step); SBE_MSG_CONSOLE("istep ", istepTableEntry->istepMajorNum, ".", step); rc = istepMap->istepWrapper(istepMap->istepHwp); } diff --git a/src/sbefw/core/printf.C b/src/sbefw/core/printf.C new file mode 100644 index 000000000..b76520f6f --- /dev/null +++ b/src/sbefw/core/printf.C @@ -0,0 +1,913 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "printf.H" + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + + +// internal _putchar wrapper +static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)buffer; (void)idx; (void)maxlen; + if (character) { + _putchar(character); + } +} + + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) { + } + else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } + else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } + else if (*format == 'o') { + base = 8U; + } + else if (*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); + } + else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char* format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int sprintf_(char* buffer, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int snprintf_(char* buffer, size_t count, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + + +int vprintf_(const char* format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + + +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = { out, arg }; + const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} diff --git a/src/sbefw/core/printf.H b/src/sbefw/core/printf.H new file mode 100644 index 000000000..13c54d0ce --- /dev/null +++ b/src/sbefw/core/printf.H @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +// SBE has no floating point support +#define PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_DISABLE_SUPPORT_EXPONENTIAL + +/** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ +void _putchar(char c); + + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ +int printf_(const char* format, ...); + + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ +int sprintf_(char* buffer, const char* format, ...); + + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ +int vprintf_(const char* format, va_list va); + + +/** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); + + +#ifdef __cplusplus +} +#endif + + +#endif // _PRINTF_H_ diff --git a/src/sbefw/core/sbeConsole.C b/src/sbefw/core/sbeConsole.C index a79974f2e..365f07fcc 100644 --- a/src/sbefw/core/sbeConsole.C +++ b/src/sbefw/core/sbeConsole.C @@ -217,6 +217,8 @@ void uartDisable(void) void uartLock(void) { + #define SBE_FUNC "uartLock" + int rcPk = PK_OK; rcPk = pk_semaphore_pend (&SBE_GLOBAL->sbeUartBinSem, PK_WAIT_FOREVER); // PK API failure @@ -227,10 +229,14 @@ void uartLock(void) rcPk, SBE_GLOBAL->sbeUartBinSem.count); pk_halt(); } + + #undef SBE_FUNC } void uartUnLock(void) { + #define SBE_FUNC "uartUnLock" + int rcPk = PK_OK; rcPk = pk_semaphore_post(&SBE_GLOBAL->sbeUartBinSem); // PK API failure @@ -241,6 +247,8 @@ void uartUnLock(void) rcPk, SBE_GLOBAL->sbeUartBinSem.count); pk_halt(); } + + #undef SBE_FUNC } void sbeMsgConsole(uint32_t num) @@ -279,3 +287,12 @@ void sbeMsgConsole(char const *msg) uartPutChar(msg[c++]); } } + +void _putchar(char c) +{ + if (!SBE_GLOBAL->sbeUartActive) + { + return; + } + uartPutChar(c); +} diff --git a/src/sbefw/core/sbeConsole.H b/src/sbefw/core/sbeConsole.H index d8b85670c..55b3e404a 100644 --- a/src/sbefw/core/sbeConsole.H +++ b/src/sbefw/core/sbeConsole.H @@ -99,6 +99,9 @@ void uartLock(void); void uartUnLock(void); void sbeMsgConsole(char const *msg); void sbeMsgConsole(uint32_t num); + +extern "C" void _putchar(char c); + // /** UART Register Offsets */ enum diff --git a/src/sbefw/core/sbeEarlyLPC.C b/src/sbefw/core/sbeEarlyLPC.C new file mode 100644 index 000000000..6c83b5509 --- /dev/null +++ b/src/sbefw/core/sbeEarlyLPC.C @@ -0,0 +1,193 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/sbefw/core/sbeConsole.C $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2018-2020 */ +/* [+] International Business Machines Corp. */ +/* [+] Raptor Engineering, LLC */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "sbetrace.H" +#include "fapi2.H" + +#include "sbeEarlyLPC.H" + +#include "p9_perv_scom_addresses.H" +#include "p9_perv_scom_addresses_fld.H" +#include "p9_misc_scom_addresses.H" +#include "p9_misc_scom_addresses_fld.H" + +#include "sberegaccess.H" +#include "sbeglobals.H" +#include "p9_lpc_utils.H" + +using namespace fapi2; + +uint32_t readLPCReg(uint16_t i_addr, + uint8_t &o_data) +{ + uint32_t rc = SBE_SEC_OPERATION_SUCCESSFUL; + if (!o_data) + return SBE_SEC_INVALID_PARAMS; + + do { + Target proc = plat_getChipTarget(); + + buffer data = 0; + ReturnCode fapiRc = lpc_rw(proc, + LPC_IO_SPACE + i_addr, + sizeof(uint8_t), + true, + false, + data); + if(fapiRc != FAPI2_RC_SUCCESS) + { + rc = SBE_SEC_LPC_ACCESS_FAILED; + o_data = 0xff; + break; + } + data.extract(o_data, 0, 8); + } while(0); + + return rc; +} + +uint32_t writeLPCReg(uint16_t i_addr, + uint8_t i_data) +{ + uint32_t rc = SBE_SEC_OPERATION_SUCCESSFUL; + + do { + Target proc = plat_getChipTarget(); + + buffer data = 0; + data.insert(i_data, 0, 8); + + ReturnCode fapiRc = lpc_rw(proc, + LPC_IO_SPACE + i_addr, + sizeof(uint8_t), + false, + false, + data); + if(fapiRc != FAPI2_RC_SUCCESS) + { + rc = SBE_SEC_LPC_ACCESS_FAILED; + break; + } + } while(0); + + return rc; +} + +uint32_t readLPCRegIndexed(uint8_t i_addr, + uint8_t i_index, uint8_t &o_data) +{ + uint32_t rc = SBE_SEC_OPERATION_SUCCESSFUL; + + do { + rc = writeLPCReg(i_addr, i_index); + if (rc != SBE_SEC_OPERATION_SUCCESSFUL) + { + o_data = 0xff; + break; + } + rc = readLPCReg(i_addr + 1, o_data); + if (rc != SBE_SEC_OPERATION_SUCCESSFUL) + { + break; + } + } while(0); + + return rc; +} + +uint32_t writeLPCRegIndexed(uint8_t i_addr, + uint8_t i_index, uint8_t i_data) +{ + uint32_t rc = SBE_SEC_OPERATION_SUCCESSFUL; + + do { + rc = writeLPCReg(i_addr, i_index); + if (rc != SBE_SEC_OPERATION_SUCCESSFUL) + { + break; + } + rc = writeLPCReg(i_addr + 1, i_data); + if (rc != SBE_SEC_OPERATION_SUCCESSFUL) + { + break; + } + } while(0); + + return rc; +} + +uint32_t clearLPCErrorStatusRegister() +{ + uint32_t rc = SBE_SEC_OPERATION_SUCCESSFUL; + + do { + Target proc = plat_getChipTarget(); + + buffer data; + ReturnCode fapiRc; + + fapiRc = lpc_read(proc, LPCM_OPB_MASTER_STATUS_REG, data); + if(fapiRc != FAPI2_RC_SUCCESS) + { + rc = SBE_SEC_LPC_ACCESS_FAILED; + break; + } + if (data & LPCM_OPB_MASTER_STATUS_ERROR_BITS) + { + // Error bits set -- clear them + data = LPCM_OPB_MASTER_STATUS_ERROR_BITS; + fapiRc = lpc_write(proc, + LPCM_OPB_MASTER_ACTUAL_STATUS_REG, + data); + if(fapiRc != FAPI2_RC_SUCCESS) + { + rc = SBE_SEC_LPC_ACCESS_FAILED; + break; + } + + data = LPCM_OPB_MASTER_STATUS_ERROR_BITS; + fapiRc = lpc_write(proc, + LPCM_OPB_MASTER_STATUS_REG, + data); + if(fapiRc != FAPI2_RC_SUCCESS) + { + rc = SBE_SEC_LPC_ACCESS_FAILED; + break; + } + + data = LPCHC_IRQSTAT_ERROR_BITS; + fapiRc = lpc_write(proc, + LPC_REG_HC_IRQSTAT, + data); + if(fapiRc != FAPI2_RC_SUCCESS) + { + rc = SBE_SEC_LPC_ACCESS_FAILED; + break; + } + } + } while(0); + + return rc; +} diff --git a/src/sbefw/core/sbeEarlyLPC.H b/src/sbefw/core/sbeEarlyLPC.H new file mode 100644 index 000000000..b5d7c0c14 --- /dev/null +++ b/src/sbefw/core/sbeEarlyLPC.H @@ -0,0 +1,55 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/sbefw/core/sbeConsole.H $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2018-2020 */ +/* [+] International Business Machines Corp. */ +/* [+] Raptor Engineering, LLC */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#pragma once + +#include "sbe_sp_intf.H" +#include "sbe_build_info.H" + +#define SBE_LPC_INIT { \ + SBE_GLOBAL->sbeLPCActive = true; \ +} + +#define SBE_LPC_DISABLE { \ + SBE_GLOBAL->sbeLPCActive = false; \ +} + +#define LPC_IO_SPACE 0xD0010000 +#define LPC_MAX_IO_SPACE (64*1024) + +#define LPCM_OPB_MASTER_STATUS_REG 0xC0010000 +#define LPCM_OPB_MASTER_ACTUAL_STATUS_REG 0xC001004C +#define LPC_REG_HC_IRQSTAT 0xC0012038 + +#define LPCM_OPB_MASTER_STATUS_ERROR_BITS 0x20000FC3 +#define LPCHC_IRQSTAT_ERROR_BITS 0x000004FC + +uint32_t readLPCReg(uint16_t i_addr, uint8_t &i_data); +uint32_t writeLPCReg(uint16_t i_addr, uint8_t i_data); +uint32_t readLPCRegIndexed(uint8_t i_addr, + uint8_t i_index, uint8_t &o_data); +uint32_t writeLPCRegIndexed(uint8_t i_addr, + uint8_t i_index, uint8_t i_data); +uint32_t clearLPCErrorStatusRegister(); diff --git a/src/sbefw/core/sbeIPLStatusLPC.C b/src/sbefw/core/sbeIPLStatusLPC.C new file mode 100644 index 000000000..9a4d6d67d --- /dev/null +++ b/src/sbefw/core/sbeIPLStatusLPC.C @@ -0,0 +1,65 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/sbefw/core/sbeConsole.C $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2018-2020 */ +/* [+] International Business Machines Corp. */ +/* [+] Raptor Engineering, LLC */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "sbetrace.H" +#include "fapi2.H" + +#include "sbeEarlyLPC.H" +#include "sbeIPLStatusLPC.H" + +#include "p9_perv_scom_addresses.H" +#include "p9_perv_scom_addresses_fld.H" +#include "p9_misc_scom_addresses.H" +#include "p9_misc_scom_addresses_fld.H" + +#include "sberegaccess.H" +#include "sbeglobals.H" +#include "p9_lpc_utils.H" + +using namespace fapi2; + +void postPutIStep(char major, char minor) +{ + #define SBE_FUNC "postPutIStep" + uint32_t rc = SBE_SEC_OPERATION_SUCCESSFUL; + do { + rc = writeLPCReg(0x81, major); + if(rc != SBE_SEC_OPERATION_SUCCESSFUL) + { + SBE_ERROR(SBE_FUNC " failure to write IPL status 1"); + break; + } + + rc = writeLPCReg(0x82, minor); + if(rc != SBE_SEC_OPERATION_SUCCESSFUL) + { + SBE_ERROR(SBE_FUNC " failure to write IPL status 2"); + break; + } + + } while(0); + + #undef SBE_FUNC +} diff --git a/src/sbefw/core/sbeIPLStatusLPC.H b/src/sbefw/core/sbeIPLStatusLPC.H new file mode 100644 index 000000000..f0970ff86 --- /dev/null +++ b/src/sbefw/core/sbeIPLStatusLPC.H @@ -0,0 +1,45 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/sbefw/core/sbeConsole.H $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2018 */ +/* [+] International Business Machines Corp. */ +/* [+] Raptor Engineering, LLC */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#pragma once + +#include "sbe_sp_intf.H" +#include "sbe_build_info.H" + +#ifndef SBE_IPL_STATUS_LPC_SUPPORT + +#define SBE_PUT_ISTEP_LPC(major, minor) + +#else + +#define SBE_PUT_ISTEP_LPC(major, minor) \ + if(SBE_GLOBAL->sbeLPCActive) \ + { \ + postPutIStep(major, minor); \ + } + +void postPutIStep(char major, char minor); + +#endif // SBE_IPL_STATUS_LPC_SUPPORT diff --git a/src/sbefw/core/sbeMemAccessInterface.C b/src/sbefw/core/sbeMemAccessInterface.C index 8b42137e6..396fb6d61 100644 --- a/src/sbefw/core/sbeMemAccessInterface.C +++ b/src/sbefw/core/sbeMemAccessInterface.C @@ -36,6 +36,7 @@ using namespace fapi2; void MEM_AVAILABLE_CHECK(uint32_t &io_available_len,uint32_t &io_len_to_send,bool &io_is_last_access) { +#define SBE_FUNC "MEM_AVAILABLE_CHECK" if(io_len_to_send > io_available_len) { SBE_INFO(SBE_FUNC" Allocated memory is less, truncating the access"); @@ -43,6 +44,7 @@ void MEM_AVAILABLE_CHECK(uint32_t &io_available_len,uint32_t &io_len_to_send,boo io_is_last_access = true; } io_available_len -= io_len_to_send; +#undef SBE_FUNC } ReturnCode sbeMemAccessInterface::setup() diff --git a/src/sbefw/core/sbeSecureMemRegionManager.C b/src/sbefw/core/sbeSecureMemRegionManager.C index ef81ca874..f6fabdb15 100644 --- a/src/sbefw/core/sbeSecureMemRegionManager.C +++ b/src/sbefw/core/sbeSecureMemRegionManager.C @@ -107,7 +107,7 @@ sbeSecondaryResponse SBESecureMemRegionManager::add(const uint64_t i_startAddr, sbeSecondaryResponse SBESecureMemRegionManager::remove(const uint64_t i_startAddr) { - #define SBE_FUNC "SBESecureMemRegionManager::remove"; + #define SBE_FUNC "SBESecureMemRegionManager::remove" size_t i = 0; sbeSecondaryResponse rc = SBE_SEC_OPERATION_SUCCESSFUL; for(; i < iv_regionsOpenCnt; i++) diff --git a/src/sbefw/core/sbeSpMsg.H b/src/sbefw/core/sbeSpMsg.H index ecd99b0d2..f43619b10 100644 --- a/src/sbefw/core/sbeSpMsg.H +++ b/src/sbefw/core/sbeSpMsg.H @@ -586,6 +586,7 @@ typedef struct */ bool validateInputArgs() { + #define SBE_FUNC "validateInputArgs" bool l_validatePassFlag = true; // Validate Thread Command / Thread Num / Error Mode if((threadOps > THREAD_SRESET_INS) || @@ -596,6 +597,7 @@ typedef struct l_validatePassFlag = false; } return l_validatePassFlag; + #undef SBE_FUNC } /** diff --git a/src/sbefw/core/sbeglobals.C b/src/sbefw/core/sbeglobals.C index d4640107c..611257c93 100644 --- a/src/sbefw/core/sbeglobals.C +++ b/src/sbefw/core/sbeglobals.C @@ -5,8 +5,9 @@ /* */ /* OpenPOWER sbe Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2017,2018 */ +/* Contributors Listed Below - COPYRIGHT 2017,2020 */ /* [+] International Business Machines Corp. */ +/* [+] Raptor Engineering, LLC */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -56,6 +57,8 @@ uint8_t SBEGlobalsSingleton::failedCmd = 0; bool SBEGlobalsSingleton::isHreset = false; +bool SBEGlobalsSingleton::sbeLPCActive = false; + #ifdef SBE_CONSOLE_SUPPORT bool SBEGlobalsSingleton::sbeUartActive = false; #endif diff --git a/src/sbefw/core/sbeglobals.H b/src/sbefw/core/sbeglobals.H index 3b2b0ad94..b20bf0768 100644 --- a/src/sbefw/core/sbeglobals.H +++ b/src/sbefw/core/sbeglobals.H @@ -5,8 +5,9 @@ /* */ /* OpenPOWER sbe Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2017,2018 */ +/* Contributors Listed Below - COPYRIGHT 2017,2020 */ /* [+] International Business Machines Corp. */ +/* [+] Raptor Engineering, LLC */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -85,6 +86,9 @@ class SBEGlobalsSingleton */ PkSemaphore sbeSemAsyncProcess; + // Binary semaphore to protect raw LPC access + static bool sbeLPCActive; + #ifdef SBE_CONSOLE_SUPPORT // Binary semaphore to protect uart access PkSemaphore sbeUartBinSem; diff --git a/src/sbefw/core/sberegaccess.C b/src/sbefw/core/sberegaccess.C index 3c327b61d..426a9f1f7 100644 --- a/src/sbefw/core/sberegaccess.C +++ b/src/sbefw/core/sberegaccess.C @@ -183,7 +183,7 @@ uint32_t SbeRegAccess::init(bool forced) rc = getscom_abs(PERV_SCRATCH_REGISTER_6_SCOM, &iv_mbx6); if(PCB_ERROR_NONE != rc) { - SBE_ERROR(SBE_FUNC"Failed reading mailbox reg 6, RC: 0x%08X. " + SBE_ERROR(SBE_FUNC"Failed reading mailbox reg 6, RC: 0x%08X. ", rc); break; } diff --git a/src/sbefw/core/sbetrace.H b/src/sbefw/core/sbetrace.H index 5c3ea7b57..d16a3abea 100644 --- a/src/sbefw/core/sbetrace.H +++ b/src/sbefw/core/sbetrace.H @@ -31,6 +31,10 @@ extern "C" { #include "pk_api.h" #include "trac_interface.h" +#ifdef SBE_CONSOLE_SUPPORT +#include "printf.H" +#endif + #ifdef __cplusplus } #endif @@ -47,6 +51,15 @@ extern "C" { #define SBE_EXIT(args...) #define SBE_DEBUG(args...) +#if defined(SBE_CONSOLE_SUPPORT) && defined(ENABLE_CONSOLE_TRACE_IN_FILE) +#define PK_TRACE_INTERNAL(args...) { \ + printf(args); \ + printf("\n\r"); \ +} +#else +#define PK_TRACE_INTERNAL(args...) PK_TRACE(args) +#endif + // Levels of logging // 0 - No tracing // 1 - Error @@ -55,21 +68,21 @@ extern "C" { // 4 - Error, info, entry/exit, debug #if (SBE_TRACE_LEVEL >= 1) #undef SBE_ERROR -#define SBE_ERROR(args...) PK_TRACE(SBE_ERR_MRK"" args) +#define SBE_ERROR(args...) PK_TRACE_INTERNAL(SBE_ERR_MRK"" args) #endif #if (SBE_TRACE_LEVEL >= 2) #undef SBE_INFO -#define SBE_INFO(args...) PK_TRACE(SBE_INF_MRK"" args) +#define SBE_INFO(args...) PK_TRACE_INTERNAL(SBE_INF_MRK"" args) #endif #if (SBE_TRACE_LEVEL >= 3) #undef SBE_ENTER #undef SBE_EXIT -#define SBE_ENTER(args...) PK_TRACE(SBE_ENTER_MRK"" args) -#define SBE_EXIT(args...) PK_TRACE(SBE_EXIT_MRK"" args) +#define SBE_ENTER(args...) PK_TRACE_INTERNAL(SBE_ENTER_MRK"" args) +#define SBE_EXIT(args...) PK_TRACE_INTERNAL(SBE_EXIT_MRK"" args) #endif #if (SBE_TRACE_LEVEL >= 4) #undef SBE_DEBUG -#define SBE_DEBUG(args...) PK_TRACE(SBE_DEBUG_MRK"" args) +#define SBE_DEBUG(args...) PK_TRACE_INTERNAL(SBE_DEBUG_MRK"" args) #endif -#define SBE_TRACE(args...) PK_TRACE(args) +#define SBE_TRACE(args...) PK_TRACE_INTERNAL(args) #endif // __SBEFW_SBE_TRACE_H