| /* IBM_PROLOG_BEGIN_TAG */ |
| /* This is an automatically generated prolog. */ |
| /* */ |
| /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/p8_pore_table_gen_api_fixed.C $ */ |
| /* */ |
| /* OpenPOWER HostBoot Project */ |
| /* */ |
| /* Contributors Listed Below - COPYRIGHT 2013,2014 */ |
| /* [+] International Business Machines Corp. */ |
| /* */ |
| /* */ |
| /* 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 */ |
| // $Id: p8_pore_table_gen_api_fixed.C,v 1.15 2014/05/30 20:31:24 cmolsen Exp $ |
| // |
| /*------------------------------------------------------------------------------*/ |
| /* *! (C) Copyright International Business Machines Corp. 2012 */ |
| /* *! All Rights Reserved -- Property of IBM */ |
| /* *! *** *** */ |
| /*------------------------------------------------------------------------------*/ |
| /* *! TITLE : p8_pore_table_gen_api_fixed.C */ |
| /* *! DESCRIPTION : PORE SLW table generaion APIs */ |
| /* *! OWNER NAME : Michael Olsen Email: cmolsen@us.ibm.com */ |
| // |
| /* *! USAGE : To build for PHYP command-line - */ |
| /* buildecmdprcd -C "p8_pore_table_gen_api_fixed.C" -c "p8_pore_table_static_data.c,sbe_xip_image.c,pore_inline_assembler.c" -u "SLW_COMMAND_LINE_RAM" p8_pore_table_gen_api_fixed_main.C */ |
| // |
| /* *! COMMENTS : - The DYNAMIC_RAM_TABLE_PPD was dropped in v1.12 of this */ |
| /* code. See v1.12 for explanation and code implementation. */ |
| // |
| /*------------------------------------------------------------------------------*/ |
| |
| #define __P8_PORE_TABLE_GEN_API_C |
| #include <p8_delta_scan_rw.h> |
| #include <p8_pore_api_custom.h> |
| #include <p8_pore_table_gen_api.H> |
| |
| /* |
| // io_image - pointer to SLW image |
| // i_modeBuild - 0: HB/IPL mode, 1: PHYP/Rebuild mode, 2: SRAM mode. |
| // i_regName - unswizzled enum SPR value (NOT a name) |
| // i_regData - data to write |
| // i_coreIndex - core ID = [0:15] |
| // i_threadIndex - thread to operate on = [0:7]. |
| */ |
| uint32_t p8_pore_gen_cpureg_fixed( void *io_image, |
| uint8_t i_modeBuild, |
| uint32_t i_regName, |
| uint64_t i_regData, |
| uint32_t i_coreId, |
| uint32_t i_threadId) |
| { |
| uint32_t rc=0, rcLoc=0, iCount=0; |
| int i=0, iReg=-1; |
| uint64_t xipSlwRamSection; |
| void *hostSlwRamSection; |
| void *hostSlwSectionFixed; |
| uint64_t xipRamTableThis; |
| void *hostRamVector; |
| void *hostRamTableThis=NULL; |
| void *hostRamEntryThis=NULL, *hostRamEntryNext=NULL; |
| uint8_t bNewTable=0, bFound=0; |
| uint8_t bEntryEnd=1, headerType=0; |
| SbeXipSection xipSection; |
| SbeXipItem xipTocItem; |
| RamTableEntry ramEntryThis, *ramEntryNext; |
| uint32_t sprSwiz=0; |
| uint8_t bReplaceEntry=0; |
| uint32_t headerNext=0; |
| uint32_t instrNext=0; |
| |
| |
| // ------------------------------------------------------------------------- |
| // Validate Ramming parameters. |
| // |
| // ...check mode build |
| if (i_modeBuild>P8_SLW_MODEBUILD_MAX_VALUE) { |
| MY_ERR("modeBuild=%i invalid. Valid range is [0;%i].", |
| i_modeBuild,P8_SLW_MODEBUILD_MAX_VALUE); |
| rcLoc = 1; |
| } |
| // ...check register value |
| bFound = 0; |
| for (i=0;i<SLW_SPR_REGS_SIZE;i++) { |
| if (i_regName==SLW_SPR_REGS[i].value) { |
| bFound = 1; |
| iReg = i; |
| break; |
| } |
| } |
| if (!bFound) { |
| MY_ERR("Register value = %i is not supported.\n",i_regName); |
| MY_ERR("The following registers are supported:\n"); |
| for (i=0;i<SLW_SPR_REGS_SIZE;i++) |
| MY_ERR("\t(%s,%i)\n",SLW_SPR_REGS[i].name,SLW_SPR_REGS[i].value); |
| rcLoc = 1; |
| } |
| // ...check core ID |
| if (i_coreId>=SLW_MAX_CORES) { |
| MY_ERR("Core ID = %i is not within valid range of [0;%i]\n",i_coreId,SLW_MAX_CORES-1); |
| rcLoc = 1; |
| } |
| // ...check thread ID |
| // - ensure it's zero if SPR is not thread scoped, i.e. if SPR is core scoped. |
| // - error out if threadId exceed max num of threads. |
| if (i_regName!=P8_SPR_HSPRG0 && i_regName!=P8_SPR_LPCR && i_regName!=P8_MSR_MSR) { |
| i_threadId = 0; |
| } |
| if (i_threadId>=SLW_CORE_THREADS) { |
| MY_ERR("Thread ID = %i is not within valid range of [0;%i]\n",i_threadId,SLW_CORE_THREADS-1); |
| rcLoc = 1; |
| } |
| if (rcLoc) |
| return IMGBUILD_ERR_RAM_INVALID_PARM; |
| rcLoc = 0; |
| |
| // ------------------------------------------------------------------------- |
| // Check slw section location and size. (Mainly needed for fixed image.) |
| // |
| if (i_modeBuild==P8_SLW_MODEBUILD_IPL || |
| i_modeBuild==P8_SLW_MODEBUILD_REBUILD) { // Fixed image. |
| hostSlwSectionFixed = (void*)( (uintptr_t)io_image + |
| FIXED_SLW_IMAGE_SIZE - |
| FIXED_FFDC_SECTION_SIZE - |
| FIXED_SLW_SECTION_SIZE ); |
| // Even though we shouldn't call this api during a rebuild, it should be |
| // safe to do so in this particular case since none of the info requested |
| // is supposed to be moved during a rebuild. |
| rc = sbe_xip_get_section( io_image, SBE_XIP_SECTION_SLW, &xipSection); |
| if (rc) { |
| MY_ERR("Probably invalid section name for SBE_XIP_SECTION_SLW.\n"); |
| return IMGBUILD_ERR_GET_SECTION; |
| } |
| hostSlwRamSection = (void*)((uintptr_t)io_image + xipSection.iv_offset); |
| if (hostSlwSectionFixed!=hostSlwRamSection) { |
| MY_ERR("hostSlwSectionFixed != hostSlwRamSection(from image api).\n"); |
| return IMGBUILD_ERR_RAM_HDRS_NOT_SYNCED; |
| } |
| else { |
| MY_INF("hostSlwSectionFixed == hostSlwRamSection(from image api).\n"); |
| } |
| } |
| else { // SRAM non-fixed image. |
| rc = sbe_xip_get_section( io_image, SBE_XIP_SECTION_SLW, &xipSection); |
| if (rc) { |
| MY_ERR("Probably invalid section name for SBE_XIP_SECTION_SLW.\n"); |
| return IMGBUILD_ERR_GET_SECTION; |
| } |
| hostSlwRamSection = (void*)((uintptr_t)io_image + xipSection.iv_offset); |
| sbe_xip_host2pore( io_image, hostSlwRamSection, &xipSlwRamSection); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Cross check SPR register and table defines |
| // |
| if (SLW_SPR_REGS_SIZE!=(SLW_MAX_CPUREGS_CORE+SLW_MAX_CPUREGS_THREADS)) { |
| MY_ERR("Defines in *.H header file not in sync.\n"); |
| return IMGBUILD_ERR_RAM_HDRS_NOT_SYNCED; |
| } |
| if (xipSection.iv_size!=FIXED_SLW_SECTION_SIZE) { |
| MY_ERR("Fixed SLW table size in *.H header file differs from SLW section size in image.\n"); |
| MY_ERR("Check code or image version.\n"); |
| return IMGBUILD_ERR_RAM_HDRS_NOT_SYNCED; |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Summarize parameters and checking results. |
| // |
| MY_INF("Input parameter checks - OK\n"); |
| MY_INF("\tMode build= %i\n",i_modeBuild); |
| MY_INF("\tRegister = (%s,%i)\n",SLW_SPR_REGS[iReg].name,SLW_SPR_REGS[iReg].value); |
| MY_INF("\tCore ID = %i\n",i_coreId); |
| MY_INF("\tThread ID = %i\n",i_threadId); |
| MY_INF("Image validation and size checks - OK\n"); |
| MY_INF("\tSLW section size= %i\n",xipSection.iv_size); |
| |
| // ------------------------------------------------------------------------- |
| // Locate RAM vector and locate RAM table associated with "This" core ID. |
| // |
| if (i_modeBuild==P8_SLW_MODEBUILD_IPL || |
| i_modeBuild==P8_SLW_MODEBUILD_REBUILD) { // Fixed image. |
| hostRamTableThis = (void*)( (uintptr_t)io_image + |
| FIXED_SLW_IMAGE_SIZE - |
| FIXED_FFDC_SECTION_SIZE - |
| FIXED_SLW_SECTION_SIZE + |
| SLW_RAM_TABLE_SPACE_PER_CORE*i_coreId ); |
| if (*(uintptr_t*)hostRamTableThis) { // Table content NOT empty. |
| bNewTable = 0; // So, NOT new table. |
| } |
| else { // Table content empty. |
| bNewTable = 1; // So, new table. |
| } |
| } |
| else { // SRAM non-fixed image. |
| rc = sbe_xip_find( io_image, SLW_HOST_REG_VECTOR_TOC_NAME, &xipTocItem); |
| if (rc) { |
| MY_ERR("Probably invalid key word for SLW_HOST_REG_VECTOR_TOC_NAME.\n"); |
| return IMGBUILD_ERR_KEYWORD_NOT_FOUND; |
| } |
| sbe_xip_pore2host( io_image, xipTocItem.iv_address, &hostRamVector); |
| xipRamTableThis = myRev64(*((uint64_t*)hostRamVector + i_coreId)); |
| if (xipRamTableThis) { |
| sbe_xip_pore2host( io_image, xipRamTableThis, &hostRamTableThis); |
| bNewTable = 0; |
| } |
| else { |
| hostRamTableThis = (void*)( (uintptr_t)hostSlwRamSection + |
| SLW_RAM_TABLE_SPACE_PER_CORE*i_coreId ); |
| bNewTable = 1; |
| } |
| } |
| |
| |
| // ------------------------------------------------------------------------- |
| // Create most of the RAM entry, so it can be used to find a potential existing entry to |
| // replace. Postpone decision about bEntryEnd and assume its zero for now (not end). |
| // |
| if (i_regName==P8_MSR_MSR) { |
| // ...make the MSR header |
| headerType = 0x1; // MTMSRD header. |
| ramEntryThis.header = ( ((uint32_t)headerType) << RAM_HEADER_TYPE_START_C & RAM_HEADER_TYPE_MASK_C ) | |
| ( i_threadId << RAM_HEADER_THREAD_START_C & RAM_HEADER_THREAD_MASK_C ); |
| // ...make the MSR instr |
| ramEntryThis.instr = RAM_MTMSRD_INSTR_TEMPL_C; |
| } |
| else { |
| // ...make the SPR header |
| headerType = 0x0; // MTSPR header. |
| ramEntryThis.header = ( ((uint32_t)headerType) << RAM_HEADER_TYPE_START_C & RAM_HEADER_TYPE_MASK_C ) | |
| ( i_regName << RAM_HEADER_SPRN_START_C & RAM_HEADER_SPRN_MASK_C ) | |
| ( i_threadId << RAM_HEADER_THREAD_START_C & RAM_HEADER_THREAD_MASK_C ); |
| // ...make the SPR instr |
| sprSwiz = i_regName>>5 | (i_regName & 0x0000001f)<<5; |
| if (sprSwiz!=SLW_SPR_REGS[iReg].swizzled) { |
| MY_ERR("Inconsistent swizzle rules implemented. Check code. Dumping data.\n"); |
| MY_ERR("\tsprSwiz (on-the-fly-calc)=%i\n",sprSwiz); |
| MY_ERR("\tSLW_SPR_REGS[%i].swizzled=%i\n",iReg,SLW_SPR_REGS[iReg].swizzled); |
| return IMGBUILD_ERR_RAM_CODE; |
| } |
| ramEntryThis.instr = RAM_MTSPR_INSTR_TEMPL_C | ( ( sprSwiz<<RAM_MTSPR_SPR_START_C ) & RAM_MTSPR_SPR_MASK_C ); |
| } |
| // ...make the data |
| ramEntryThis.data = i_regData; |
| |
| |
| |
| // ------------------------------------------------------------------------- |
| // Determine insertion point of new RAM entry, hostRamEntryThis. The possibilities are: |
| // - New table => First entry |
| // - Existing Ram entry => Replace said entry |
| // - Existing table, new Ram entry => Last entry |
| // |
| bReplaceEntry = 0; |
| if (bNewTable) { |
| // Append to beginning of agreed upon static Ram table position for this coreId. |
| bEntryEnd = 1; |
| ramEntryThis.header = ( ((uint32_t)bEntryEnd) << RAM_HEADER_END_START_C & RAM_HEADER_END_MASK_C ) | |
| ramEntryThis.header; |
| hostRamEntryThis = hostRamTableThis; |
| if (i_modeBuild==P8_SLW_MODEBUILD_SRAM) { |
| // Update RAM vector (since it is currently NULL) |
| *((uint64_t*)hostRamVector + i_coreId) = |
| myRev64( xipSlwRamSection + SLW_RAM_TABLE_SPACE_PER_CORE*i_coreId ); |
| } |
| } |
| else { |
| // Append at end of existing Ram table for this coreId |
| // or |
| // Replace an existing Ram entry |
| hostRamEntryNext = hostRamTableThis; |
| ramEntryNext = (RamTableEntry*)hostRamEntryNext; |
| headerNext = myRev32(ramEntryNext->header); |
| instrNext = myRev32(ramEntryNext->instr); |
| iCount = 1; |
| // Examine all entries, except last entry. |
| while ((headerNext & RAM_HEADER_END_MASK_C)==0 && bReplaceEntry==0) { |
| if (iCount>=SLW_MAX_CPUREGS_OPS) { |
| MY_ERR("Bad table! Header end bit not found and RAM table full (=%i entries).\n",SLW_MAX_CPUREGS_OPS); |
| return IMGBUILD_ERR_RAM_TABLE_END_NOT_FOUND; |
| } |
| if (ramEntryThis.header==headerNext && ramEntryThis.instr==instrNext) { |
| // Its a replacement. Stop searching. Go do the replacement. |
| bReplaceEntry = 1; |
| hostRamEntryThis = hostRamEntryNext; |
| } |
| else { |
| hostRamEntryNext = (void*)((uint8_t*)hostRamEntryNext + XIPSIZE_RAM_ENTRY); |
| ramEntryNext = (RamTableEntry*)hostRamEntryNext; |
| headerNext = myRev32(ramEntryNext->header); |
| instrNext = myRev32(ramEntryNext->instr); |
| iCount++; |
| } |
| } |
| if (bReplaceEntry==0) { |
| // Examine the last entry. |
| if (headerNext & RAM_HEADER_END_MASK_C) { |
| // Now we know for sure that our new Ram entry will also be the last, either as a |
| // replace or append. So put the end bit into the new entry. |
| bEntryEnd = 1; |
| ramEntryThis.header = ( ((uint32_t)bEntryEnd) << RAM_HEADER_END_START_C & RAM_HEADER_END_MASK_C ) | |
| ramEntryThis.header; |
| // Determine if to replace or append. |
| if (ramEntryThis.header==headerNext && ramEntryThis.instr==instrNext) { |
| // Its a replacement. And it would be legal to replace the very last Ram in a completely full table. |
| if (iCount<=SLW_MAX_CPUREGS_OPS) { |
| bReplaceEntry = 1; |
| hostRamEntryThis = hostRamEntryNext; |
| } |
| else { |
| MY_ERR("RAM table is full. Max %i entries allowed.\n",SLW_MAX_CPUREGS_OPS); |
| return IMGBUILD_ERR_RAM_TABLE_FULL; |
| } |
| } |
| else { |
| // Its an append. Make sure there's room for one more Ram entry. |
| if (iCount<SLW_MAX_CPUREGS_OPS) { |
| // Zero out the end bit in last entrys header (which will now be 2nd last). |
| ramEntryNext->header = ramEntryNext->header & myRev32(~RAM_HEADER_END_MASK_C); |
| hostRamEntryThis = (void*)((uint8_t*)hostRamEntryNext + XIPSIZE_RAM_ENTRY); |
| } |
| else { |
| MY_ERR("RAM table is full. Max %i entries allowed.\n",SLW_MAX_CPUREGS_OPS); |
| return IMGBUILD_ERR_RAM_TABLE_FULL; |
| } |
| } |
| } |
| else { |
| MY_ERR("We should never get here. Check code. Dumping data:\n"); |
| MY_ERR("myRev32(ramEntryNext->header) = 0x%08x\n",myRev32(ramEntryNext->header)); |
| MY_ERR("RAM_HEADER_END_MASK_C = 0x%08x\n",RAM_HEADER_END_MASK_C); |
| return IMGBUILD_ERR_RAM_CODE; |
| } |
| } |
| } |
| |
| |
| // Summarize new table entry data |
| MY_INF("New table entry data (host format):\n"); |
| MY_INF("\theader = 0x%08x\n",ramEntryThis.header); |
| MY_INF("\tinstr = 0x%08x\n",ramEntryThis.instr); |
| MY_INF("\tdata = 0x%016llx\n",ramEntryThis.data); |
| |
| // ------------------------------------------------------------------------- |
| // Insert the new RAM entry into the table in BE format. |
| // |
| ramEntryNext = (RamTableEntry*)hostRamEntryThis; |
| // ...some redundant checking |
| if (bNewTable) { |
| // For any new table, the insertion location should be clean. We check for this here. |
| if (myRev32(ramEntryNext->header)!=0) { |
| MY_ERR("WARNING : Table entry location should be empty for a new table. Check code and image. Dumping data:\n"); |
| MY_ERR("\theader = 0x%08x\n",myRev32(ramEntryNext->header)); |
| MY_ERR("\tinstr = 0x%08x\n",myRev32(ramEntryNext->instr)); |
| MY_ERR("\tdata = 0x%016llx\n",myRev64(ramEntryNext->data)); |
| rc = IMGBUILD_WARN_RAM_TABLE_CONTAMINATION; |
| } |
| } |
| // ..insert the new Ram entry. |
| ramEntryNext->header = myRev32(ramEntryThis.header); |
| ramEntryNext->instr = myRev32(ramEntryThis.instr); |
| ramEntryNext->data = myRev64(ramEntryThis.data); |
| |
| return rc; |
| } |
| |
| |
| /* |
| // io_image - Pointer to SLW image. |
| // i_modeBuild - 0: HB/IPL mode, 1: PHYP/Rebuild mode, 2: SRAM mode. |
| // i_scomAddr - Scom address. |
| // i_coreId - The core ID [0:15]. |
| // i_scomData - Data to write to scom register. |
| // i_operation - What to do with the scom addr and data. |
| // i_section - 0: General Scoms, 1: L2 cache, 2: L3 cache. |
| */ |
| uint32_t p8_pore_gen_scom_fixed(void *io_image, |
| uint8_t i_modeBuild, |
| uint32_t i_scomAddr, |
| uint32_t i_coreId, // [0:15] |
| uint64_t i_scomData, |
| uint32_t i_operation, // [0:7] |
| uint32_t i_section) // [0,1,2] |
| { |
| uint32_t rc=0, rcLoc=0, iEntry=0; |
| uint32_t chipletId=0; |
| uint32_t operation=0; |
| uint32_t entriesCount=0, entriesMatch=0, entriesNOP=0; |
| void *hostSlwSection; |
| void *hostSlwSectionFixed; |
| uint64_t xipScomTableThis; |
| void *hostScomVector, *hostScomTableThis; |
| void *hostScomEntryNext; // running entry pointer |
| void *hostScomEntryMatch=NULL; // pointer to entry that matches scomAddr |
| void *hostScomEntryRET=NULL; // pointer to first return instr after table |
| void *hostScomEntryNOP=NULL; // pointer to first nop IIS |
| uint8_t bufIIS[XIPSIZE_SCOM_ENTRY], bufNOP[4], bufRET[4]; |
| SbeXipSection xipSection; |
| SbeXipItem xipTocItem; |
| PoreInlineContext ctx; |
| |
| // ------------------------------------------------------------------------- |
| // Validate Scom parameters. |
| // |
| // ...check if valid Scom register (is there anything we can do here to check?) |
| // Skipping check. We blindly trust caller. |
| // |
| // ...check mode build |
| if (i_modeBuild>P8_SLW_MODEBUILD_MAX_VALUE) { |
| MY_ERR("modeBuild=%i invalid. Valid range is [0;%i].", |
| i_modeBuild,P8_SLW_MODEBUILD_MAX_VALUE); |
| rcLoc = 1; |
| } |
| // ...check Scom operation |
| if (i_operation>P8_PORE_SCOM_LAST_OP) { |
| MY_ERR("Scom operation = %i is not within valid range of [%d;%d]\n", |
| i_operation, P8_PORE_SCOM_FIRST_OP, P8_PORE_SCOM_LAST_OP); |
| rcLoc = 1; |
| } |
| // ...check that core ID corresponds to valid chiplet ID |
| chipletId = i_coreId + P8_CID_EX_LOW; |
| if (chipletId<P8_CID_EX_LOW || chipletId>P8_CID_EX_HIGH) { |
| MY_ERR("Chiplet ID = 0x%02x is not within valid range of [0x%02x;0x%02x]\n", |
| chipletId, P8_CID_EX_LOW, P8_CID_EX_HIGH); |
| rcLoc = 1; |
| } |
| if (rcLoc) |
| return IMGBUILD_ERR_SCOM_INVALID_PARM; |
| rcLoc = 0; |
| |
| // ------------------------------------------------------------------------- |
| // Check slw section location and size. (Mainly needed for fixed image.) |
| // |
| if (i_modeBuild==P8_SLW_MODEBUILD_IPL || |
| i_modeBuild==P8_SLW_MODEBUILD_REBUILD) { // Fixed image. |
| hostSlwSectionFixed = (void*)( (uintptr_t)io_image + |
| FIXED_SLW_IMAGE_SIZE - |
| FIXED_FFDC_SECTION_SIZE - |
| FIXED_SLW_SECTION_SIZE ); |
| // Even though we shouldn't call this api during a rebuild, it should be |
| // safe to do so in this particular case since none of the info requested |
| // is supposed to be moved during a rebuild. |
| rc = sbe_xip_get_section( io_image, SBE_XIP_SECTION_SLW, &xipSection); |
| if (rc) { |
| MY_ERR("Probably invalid section name for SBE_XIP_SECTION_SLW.\n"); |
| return IMGBUILD_ERR_GET_SECTION; |
| } |
| hostSlwSection = (void*)((uintptr_t)io_image + xipSection.iv_offset); |
| if (hostSlwSectionFixed!=hostSlwSection) { |
| MY_ERR("hostSlwSectionFixed != hostSlwSection(from image api).\n"); |
| return IMGBUILD_ERR_SCOM_HDRS_NOT_SYNCD; |
| } |
| else { |
| MY_INF("hostSlwSectionFixed == hostSlwSection(from image api).\n"); |
| } |
| } |
| else { // SRAM non-fixed image. |
| rc = sbe_xip_get_section( io_image, SBE_XIP_SECTION_SLW, &xipSection); |
| if (rc) { |
| MY_ERR("Probably invalid section name for SBE_XIP_SECTION_SLW.\n"); |
| return IMGBUILD_ERR_GET_SECTION; |
| } |
| hostSlwSection = (void*)((uintptr_t)io_image + xipSection.iv_offset); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Check .slw section size and cross-check w/header define. |
| // |
| if (xipSection.iv_size!=FIXED_SLW_SECTION_SIZE) { |
| MY_ERR("SLW table size in *.H header file (=%ld) differs from SLW section size in image (=%i).\n",FIXED_SLW_SECTION_SIZE,xipSection.iv_size); |
| MY_ERR("Check code or image version.\n"); |
| return IMGBUILD_ERR_SCOM_HDRS_NOT_SYNCD; |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Summarize parameters and checking results. |
| // |
| MY_INF("Input parameter checks - OK\n"); |
| MY_INF("\tRegister = 0x%08x\n",i_scomAddr); |
| MY_INF("\tOperation = %i\n",i_operation); |
| MY_INF("\tSection = %i\n",i_section); |
| MY_INF("\tCore ID = %i\n",i_coreId); |
| MY_INF("Image validation and size checks - OK\n"); |
| MY_INF("\tSLW section size= %i\n",xipSection.iv_size); |
| |
| // ------------------------------------------------------------------------- |
| // Locate Scom vector according to i_section and then locate Scom table |
| // associated with "This" core ID. |
| // |
| if (i_modeBuild==P8_SLW_MODEBUILD_IPL || |
| i_modeBuild==P8_SLW_MODEBUILD_REBUILD) { // Fixed image. |
| switch (i_section) { |
| case P8_SCOM_SECTION_NC: |
| hostScomTableThis = (void*)( (uintptr_t)hostSlwSection + |
| SLW_RAM_TABLE_SIZE + |
| SLW_SCOM_TABLE_SPACE_PER_CORE_NC*i_coreId ); |
| break; |
| case P8_SCOM_SECTION_L2: |
| hostScomTableThis = (void*)( (uintptr_t)hostSlwSection + |
| SLW_RAM_TABLE_SIZE + |
| SLW_SCOM_TABLE_SIZE_NC + |
| SLW_SCOM_TABLE_SPACE_PER_CORE_L2*i_coreId ); |
| break; |
| case P8_SCOM_SECTION_L3: |
| hostScomTableThis = (void*)( (uintptr_t)hostSlwSection + |
| SLW_RAM_TABLE_SIZE + |
| SLW_SCOM_TABLE_SIZE_NC + |
| SLW_SCOM_TABLE_SIZE_L2 + |
| SLW_SCOM_TABLE_SPACE_PER_CORE_L3*i_coreId ); |
| break; |
| default: |
| MY_ERR("Invalid value for i_section (=%i).\n",i_section); |
| MY_ERR("Valid values for i_section = [%i,%i,%i].\n", |
| P8_SCOM_SECTION_NC,P8_SCOM_SECTION_L2,P8_SCOM_SECTION_L3); |
| return IMGBUILD_ERR_SCOM_INVALID_SUBSECTION; |
| break; |
| } |
| } |
| else { // SRAM non-fixed image. |
| switch (i_section) { |
| case P8_SCOM_SECTION_NC: |
| rc = sbe_xip_find( io_image, SLW_HOST_SCOM_NC_VECTOR_TOC_NAME, &xipTocItem); |
| if (rc) { |
| MY_ERR("Probably invalid key word for SLW_HOST_SCOM_NC_VECTOR_TOC_NAME.\n"); |
| return IMGBUILD_ERR_KEYWORD_NOT_FOUND; |
| } |
| break; |
| case P8_SCOM_SECTION_L2: |
| rc = sbe_xip_find( io_image, SLW_HOST_SCOM_L2_VECTOR_TOC_NAME, &xipTocItem); |
| if (rc) { |
| MY_ERR("Probably invalid key word for SLW_HOST_SCOM_L2_VECTOR_TOC_NAME.\n"); |
| return IMGBUILD_ERR_KEYWORD_NOT_FOUND; |
| } |
| break; |
| case P8_SCOM_SECTION_L3: |
| rc = sbe_xip_find( io_image, SLW_HOST_SCOM_L3_VECTOR_TOC_NAME, &xipTocItem); |
| if (rc) { |
| MY_ERR("Probably invalid key word for SLW_HOST_SCOM_L3_VECTOR_TOC_NAME.\n"); |
| return IMGBUILD_ERR_KEYWORD_NOT_FOUND; |
| } |
| break; |
| default: |
| MY_ERR("Invalid value for i_section (=%i).\n",i_section); |
| MY_ERR("Valid values for i_section = [%i,%i,%i].\n", |
| P8_SCOM_SECTION_NC,P8_SCOM_SECTION_L2,P8_SCOM_SECTION_L3); |
| return IMGBUILD_ERR_SCOM_INVALID_SUBSECTION; |
| } |
| MY_INF("xipTocItem.iv_address = 0x%016llx\n",xipTocItem.iv_address); |
| sbe_xip_pore2host( io_image, xipTocItem.iv_address, &hostScomVector); |
| MY_INF("hostScomVector = 0x%016llx\n",(uint64_t)hostScomVector); |
| xipScomTableThis = myRev64(*((uint64_t*)hostScomVector + i_coreId)); |
| MY_INF("xipScomTableThis = 0x%016llx\n",xipScomTableThis); |
| if (xipScomTableThis) { |
| sbe_xip_pore2host( io_image, xipScomTableThis, &hostScomTableThis); |
| } |
| else { // Should never be here. |
| MY_ERR("Code or image bug. Scom vector table entries should never be null.\n"); |
| return IMGBUILD_ERR_CHECK_CODE; |
| } |
| } |
| |
| // |
| // Determine where to place/do Scom action and if entry already exists. |
| // Insertion rules: |
| // - If entry doesn't exist, insert at first NOP. (Note that if you don't do |
| // this, then the table might potentially overflow since the max table size |
| // doesn't include NOP entries.) |
| // - If no NOP found, insert at first RET. |
| // |
| |
| //---------------------------------------------------------------------------- |
| // 1. Create search strings for addr, nop and ret. |
| //---------------------------------------------------------------------------- |
| // Note, the following IIS will also be used in case of |
| // - i_operation==append |
| // - i_operation==replace |
| pore_inline_context_create( &ctx, (void*)bufIIS, XIPSIZE_SCOM_ENTRY, 0, 0); |
| pore_LS( &ctx, P1, chipletId); |
| pore_STI( &ctx, i_scomAddr, P1, i_scomData); |
| if (ctx.error > 0) { |
| MY_ERR("pore_LS or _STI generated rc = %d", ctx.error); |
| return IMGBUILD_ERR_PORE_INLINE_ASM; |
| } |
| pore_inline_context_create( &ctx, (void*)bufRET, 4, 0, 0); |
| pore_RET( &ctx); |
| if (ctx.error > 0) { |
| MY_ERR("pore_RET generated rc = %d", ctx.error); |
| return IMGBUILD_ERR_PORE_INLINE_ASM; |
| } |
| pore_inline_context_create( &ctx, (void*)bufNOP, 4, 0, 0); |
| pore_NOP( &ctx); |
| if (ctx.error > 0) { |
| MY_ERR("pore_NOP generated rc = %d", ctx.error); |
| return IMGBUILD_ERR_PORE_INLINE_ASM; |
| } |
| |
| //---------------------------------------------------------------------------- |
| // 2. Search for addr and nop in relevant coreId table until first RET. |
| //---------------------------------------------------------------------------- |
| // Note: |
| // - We go through ALL entries until first RET instr. We MUST find a RET instr, |
| // though we don't check for overrun until later. (Could be improved.) |
| // - Count number of entries, incl the NOOPs, until we find an RET. |
| // - The STI(+SCOM_addr) opcode is in the 2nd word of the Scom entry. |
| // - For an append operation, if a NOP is found (before a RET obviously), the |
| // SCOM is replacing that NNNN sequence. |
| hostScomEntryNext = hostScomTableThis; |
| MY_INF("hostScomEntryNext (addr): 0x%016llx\n ",(uint64_t)hostScomEntryNext); |
| while (memcmp(hostScomEntryNext, bufRET, sizeof(uint32_t))) { |
| entriesCount++; |
| MY_INF("Number of SCOM entries: %i\n ",entriesCount); |
| if (*((uint32_t*)bufIIS+1)==*((uint32_t*)hostScomEntryNext+1) && entriesMatch==0) {// +1 skips 1st word in Scom entry (which loads the PC in an LS operation.) |
| hostScomEntryMatch = hostScomEntryNext; |
| entriesMatch++; |
| } |
| if (memcmp(hostScomEntryNext, bufNOP, sizeof(uint32_t))==0 && entriesNOP==0) { |
| hostScomEntryNOP = hostScomEntryNext; |
| entriesNOP++; |
| } |
| hostScomEntryNext = (void*)((uintptr_t)hostScomEntryNext + XIPSIZE_SCOM_ENTRY); |
| } |
| hostScomEntryRET = hostScomEntryNext; // The last EntryNext is always the first RET. |
| |
| //---------------------------------------------------------------------------- |
| // 3. Qualify (translate) operation and IIS. |
| //---------------------------------------------------------------------------- |
| if (i_operation==P8_PORE_SCOM_APPEND) |
| { |
| operation = i_operation; |
| } |
| else if (i_operation==P8_PORE_SCOM_REPLACE) |
| { |
| if (hostScomEntryMatch) |
| // ... do a replace |
| operation = i_operation; |
| else |
| // ... do an append |
| operation = P8_PORE_SCOM_APPEND; |
| } |
| else if (i_operation==P8_PORE_SCOM_NOOP) |
| { |
| // ...overwrite earlier bufIIS from the search step |
| pore_inline_context_create( &ctx, (void*)bufIIS, XIPSIZE_SCOM_ENTRY, 0, 0); |
| pore_NOP( &ctx); |
| pore_NOP( &ctx); |
| pore_NOP( &ctx); |
| pore_NOP( &ctx); |
| if (ctx.error > 0) { |
| MY_ERR("*** _NOP generated rc = %d", ctx.error); |
| return IMGBUILD_ERR_PORE_INLINE_ASM; |
| } |
| operation = i_operation; |
| } |
| else if ( i_operation==P8_PORE_SCOM_AND || |
| i_operation==P8_PORE_SCOM_OR ) |
| { |
| operation = i_operation; |
| } |
| else if ( i_operation==P8_PORE_SCOM_AND_APPEND ) |
| { |
| if (hostScomEntryMatch) |
| // ... do the AND on existing Scom |
| operation = P8_PORE_SCOM_AND; |
| else |
| // ... do an append (this better be to an _AND register type) |
| operation = P8_PORE_SCOM_APPEND; |
| } |
| else if ( i_operation==P8_PORE_SCOM_OR_APPEND ) |
| { |
| if (hostScomEntryMatch) |
| // ... do the OR on existing Scom |
| operation = P8_PORE_SCOM_OR; |
| else |
| // ... do an append (this better be to an _OR register type) |
| operation = P8_PORE_SCOM_APPEND; |
| } |
| else if (i_operation==P8_PORE_SCOM_RESET) |
| { |
| // ... create RNNN instruction sequence. |
| pore_inline_context_create( &ctx, (void*)bufIIS, XIPSIZE_SCOM_ENTRY, 0, 0); |
| pore_RET( &ctx); |
| pore_NOP( &ctx); |
| pore_NOP( &ctx); |
| pore_NOP( &ctx); |
| if (ctx.error > 0) { |
| MY_ERR("***_RET or _NOP generated rc = %d", ctx.error); |
| return IMGBUILD_ERR_PORE_INLINE_ASM; |
| } |
| operation = i_operation; |
| } |
| else |
| { |
| MY_ERR("Scom operation = %i is not within valid range of [%d;%d]\n", |
| i_operation, P8_PORE_SCOM_FIRST_OP, P8_PORE_SCOM_LAST_OP); |
| return IMGBUILD_ERR_SCOM_INVALID_PARM; |
| } |
| |
| //---------------------------------------------------------------------------- |
| // 4. Check for overrun. |
| //---------------------------------------------------------------------------- |
| // Note: |
| // - An entry count exceeding the max allocated entry count will result in a code error |
| // because the allocation is based on an agreed upon max number of entries and |
| // therefore either the code header file needs to change or the caller is not abiding |
| // by the rules. |
| // - An entry count equalling the max allocated entry count is allowed for all commands |
| // except the APPEND command, incl the translated REPLACE->APPEND, which will result |
| // in the previously mentioned code error being returned. |
| // - The table can be full but still include NOOPs. If so, we can still APPEND since |
| // we append at first occurrence of a NOOP or at the end of the table (at the RET). |
| switch (i_section) { |
| case P8_SCOM_SECTION_NC: |
| if ( ( (operation==P8_PORE_SCOM_APPEND && entriesCount==SLW_MAX_SCOMS_NC) && |
| hostScomEntryNOP==NULL ) || |
| entriesCount>SLW_MAX_SCOMS_NC ) |
| { |
| MY_ERR("SCOM table NC is full. Max %i entries allowed.\n",SLW_MAX_SCOMS_NC); |
| return IMGBUILD_ERR_CHECK_CODE; |
| } |
| break; |
| case P8_SCOM_SECTION_L2: |
| if ( ( (operation==P8_PORE_SCOM_APPEND && entriesCount==SLW_MAX_SCOMS_L2) && |
| hostScomEntryNOP==NULL ) || |
| entriesCount>SLW_MAX_SCOMS_L2 ) |
| { |
| MY_ERR("SCOM table L2 is full. Max %i entries allowed.\n",SLW_MAX_SCOMS_L2); |
| return IMGBUILD_ERR_CHECK_CODE; |
| } |
| break; |
| case P8_SCOM_SECTION_L3: |
| if ( ( (operation==P8_PORE_SCOM_APPEND && entriesCount==SLW_MAX_SCOMS_L3) && |
| hostScomEntryNOP==NULL ) || |
| entriesCount>SLW_MAX_SCOMS_L3 ) |
| { |
| MY_ERR("SCOM table L3 is full. Max %i entries allowed.\n",SLW_MAX_SCOMS_L3); |
| return IMGBUILD_ERR_CHECK_CODE; |
| } |
| break; |
| default: |
| MY_ERR("Invalid value for i_section (=%i).\n",i_section); |
| MY_ERR("Valid values for i_section = [%i,%i,%i].\n", |
| P8_SCOM_SECTION_NC,P8_SCOM_SECTION_L2,P8_SCOM_SECTION_L3); |
| return IMGBUILD_ERR_SCOM_INVALID_SUBSECTION; |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // 5. Insert the SCOM. |
| // --------------------------------------------------------------------------- |
| // Assuming pre-allocated Scom table (after pre-allocated Ram table): |
| // - Table is pre-filled with RNNN ISS. |
| // - Each core Id has dedicated space, uniformly distributed by SLW_MAX_SCOMS_NC* |
| // XIPSIZE_SCOM_ENTRY. |
| // - Remember to check for more than SLW_MAX_SCOMS_NC entries! |
| switch (operation) { |
| |
| case P8_PORE_SCOM_APPEND: // Append a Scom at first occurring NNNN or RNNN, |
| if (hostScomEntryNOP) { |
| // ... replace the NNNN |
| MY_INF("Append at NOP\n"); |
| memcpy(hostScomEntryNOP,(void*)bufIIS,XIPSIZE_SCOM_ENTRY); |
| } |
| else if (hostScomEntryRET) { |
| // ... replace the RNNN |
| MY_INF("Append at RET\n"); |
| memcpy(hostScomEntryRET,(void*)bufIIS,XIPSIZE_SCOM_ENTRY); |
| } |
| else { |
| // We should never be here. |
| MY_ERR("In case=_SCOM_APPEND: EntryRET=NULL is impossible. Check code.\n"); |
| return IMGBUILD_ERR_CHECK_CODE; |
| } |
| break; |
| case P8_PORE_SCOM_REPLACE: // Replace existing Scom with new data |
| if (hostScomEntryMatch) { |
| // ... do a vanilla replace |
| MY_INF("Replace existing Scom\n"); |
| memcpy(hostScomEntryMatch,(void*)bufIIS,XIPSIZE_SCOM_ENTRY); |
| } |
| else { |
| // We should never be here. |
| MY_ERR("In case=_SCOM_REPLACE: EntryMatch=NULL is impossible. Check code.\n"); |
| return IMGBUILD_ERR_CHECK_CODE; |
| } |
| break; |
| case P8_PORE_SCOM_NOOP: |
| if (hostScomEntryMatch) { |
| // ... do a vanilla replace |
| MY_INF("Replace existing Scom w/NOPs\n"); |
| memcpy(hostScomEntryMatch,(void*)bufIIS,XIPSIZE_SCOM_ENTRY); |
| } |
| else { |
| MY_ERR("No Scom entry found to replace NOOPs with.\n"); |
| return IMGBUILD_ERR_SCOM_ENTRY_NOT_FOUND; |
| } |
| break; |
| case P8_PORE_SCOM_OR: // Overlay Scom data onto existing data by bitwise OR |
| if (hostScomEntryMatch) { |
| // ... do an OR on the data (which is the 2nd DWord in the entry) |
| MY_INF("Overlay existing Scom - OR case\n"); |
| *((uint64_t*)hostScomEntryMatch+1) = |
| *((uint64_t*)hostScomEntryMatch+1) | myRev64(i_scomData); |
| } |
| else { |
| MY_ERR("No Scom entry found to do OR operation with.\n"); |
| return IMGBUILD_ERR_SCOM_ENTRY_NOT_FOUND; |
| } |
| break; |
| case P8_PORE_SCOM_AND: // Overlay Scom data onto existing data by bitwise AND |
| if (hostScomEntryMatch) { |
| // ... do an AND on the data (which is the 2nd DWord in the entry) |
| MY_INF("Overlay existing Scom - AND case\n"); |
| *((uint64_t*)hostScomEntryMatch+1) = |
| *((uint64_t*)hostScomEntryMatch+1) & myRev64(i_scomData); |
| } |
| else { |
| MY_ERR("No Scom entry found to do AND operation with.\n"); |
| return IMGBUILD_ERR_SCOM_ENTRY_NOT_FOUND; |
| } |
| break; |
| case P8_PORE_SCOM_RESET: // Reset (delete) table. Refill w/RNNN ISS. |
| MY_INF("Reset table\n"); |
| hostScomEntryNext = hostScomTableThis; |
| for ( iEntry=0; iEntry<entriesCount; iEntry++) { |
| memcpy( hostScomEntryNext, (void*)bufIIS, XIPSIZE_SCOM_ENTRY); |
| hostScomEntryNext = (void*)((uintptr_t)hostScomEntryNext + XIPSIZE_SCOM_ENTRY); |
| } |
| break; |
| default: |
| MY_ERR("Impossible value of operation (=%i). Check code.\n",operation); |
| return IMGBUILD_ERR_CHECK_CODE; |
| |
| } // End of switch(operation) |
| |
| return rc; |
| } |