| /*************************************************************************/ /*! |
| @File |
| @Title Services Firmware image utilities used at init time |
| @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved |
| @Description Services Firmware image utilities used at init time |
| @License Dual MIT/GPLv2 |
| |
| The contents of this file are subject to the MIT license as set out below. |
| |
| 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. |
| |
| Alternatively, the contents of this file may be used under the terms of |
| the GNU General Public License Version 2 ("GPL") in which case the provisions |
| of GPL are applicable instead of those above. |
| |
| If you wish to allow use of your version of this file only under the terms of |
| GPL, and not to allow others to use your version of this file under the terms |
| of the MIT license, indicate your decision by deleting the provisions above |
| and replace them with the notice and other provisions required by GPL as set |
| out in the file called "GPL-COPYING" included in this distribution. If you do |
| not delete the provisions above, a recipient may use your version of this file |
| under the terms of either the MIT license or GPL. |
| |
| This License is also included in this distribution in the file called |
| "MIT-COPYING". |
| |
| EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) 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; AND (B) 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. |
| */ /**************************************************************************/ |
| |
| /* The routines implemented here are built on top of an abstraction layer to |
| * hide DDK/OS-specific details in case they are used outside of the DDK |
| * (e.g. when trusted device is enabled). |
| * Any new dependency should be added to rgxlayer.h. |
| * Any new code should be built on top of the existing abstraction layer, |
| * which should be extended when necessary. */ |
| #include "rgxfwimageutils.h" |
| #include "pvrsrv.h" |
| |
| |
| /************************************************************************ |
| * FW layout information |
| ************************************************************************/ |
| #define MAX_NUM_ENTRIES (8) |
| static RGX_FW_LAYOUT_ENTRY asRGXFWLayoutTable[MAX_NUM_ENTRIES]; |
| static IMG_UINT32 ui32LayoutEntryNum; |
| |
| |
| static RGX_FW_LAYOUT_ENTRY* GetTableEntry(const void *hPrivate, RGX_FW_SECTION_ID eId) |
| { |
| IMG_UINT32 i; |
| |
| for (i = 0; i < ui32LayoutEntryNum; i++) |
| { |
| if (asRGXFWLayoutTable[i].eId == eId) |
| { |
| return &asRGXFWLayoutTable[i]; |
| } |
| } |
| |
| RGXErrorLog(hPrivate, "%s: id %u not found, returning entry 0\n", |
| __func__, eId); |
| |
| return &asRGXFWLayoutTable[0]; |
| } |
| |
| /*! |
| ******************************************************************************* |
| |
| @Function FindMMUSegment |
| |
| @Description Given a 32 bit FW address attempt to find the corresponding |
| pointer to FW allocation |
| |
| @Input ui32OffsetIn : 32 bit FW address |
| @Input pvHostFWCodeAddr : Pointer to FW code |
| @Input pvHostFWDataAddr : Pointer to FW data |
| @Input uiHostAddrOut : CPU pointer equivalent to ui32OffsetIn |
| |
| @Return PVRSRV_ERROR |
| |
| ******************************************************************************/ |
| static PVRSRV_ERROR FindMMUSegment(IMG_UINT32 ui32OffsetIn, |
| void *pvHostFWCodeAddr, |
| void *pvHostFWDataAddr, |
| void **uiHostAddrOut) |
| { |
| IMG_UINT32 i; |
| |
| for (i = 0; i < ui32LayoutEntryNum; i++) |
| { |
| if ((ui32OffsetIn >= asRGXFWLayoutTable[i].ui32BaseAddr) && |
| (ui32OffsetIn < (asRGXFWLayoutTable[i].ui32BaseAddr + asRGXFWLayoutTable[i].ui32AllocSize))) |
| { |
| if (asRGXFWLayoutTable[i].eType == FW_CODE) |
| { |
| *uiHostAddrOut = pvHostFWCodeAddr; |
| } |
| else |
| { |
| *uiHostAddrOut = pvHostFWDataAddr; |
| } |
| goto found; |
| } |
| } |
| |
| return PVRSRV_ERROR_INIT_FAILURE; |
| |
| found: |
| if (*uiHostAddrOut == NULL) |
| { |
| return PVRSRV_OK; |
| } |
| |
| /* Direct Mem write to mapped memory */ |
| ui32OffsetIn -= asRGXFWLayoutTable[i].ui32BaseAddr; |
| ui32OffsetIn += asRGXFWLayoutTable[i].ui32AllocOffset; |
| |
| /* Add offset to pointer to FW allocation only if |
| * that allocation is available |
| */ |
| if (*uiHostAddrOut) |
| { |
| *(IMG_UINT8 **)uiHostAddrOut += ui32OffsetIn; |
| } |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| /*! |
| ******************************************************************************* |
| |
| @Function RGXFWConfigureSegID |
| |
| @Description Configures a single segment of the Segment MMU |
| (base, limit and out_addr) |
| |
| @Input hPrivate : Implementation specific data |
| @Input ui64SegOutAddr : Segment output base address (40 bit devVaddr) |
| @Input ui32SegBase : Segment input base address (32 bit FW address) |
| @Input ui32SegLimit : Segment size |
| @Input ui32SegID : Segment ID |
| @Input pszName : Segment name |
| @Input ppui32BootConf : Pointer to bootloader data |
| |
| @Return void |
| |
| ******************************************************************************/ |
| static void RGXFWConfigureSegID(const void *hPrivate, |
| IMG_UINT64 ui64SegOutAddr, |
| IMG_UINT32 ui32SegBase, |
| IMG_UINT32 ui32SegLimit, |
| IMG_UINT32 ui32SegID, |
| IMG_UINT32 **ppui32BootConf) |
| { |
| IMG_UINT32 *pui32BootConf = *ppui32BootConf; |
| IMG_UINT32 ui32SegOutAddr0 = ui64SegOutAddr & 0x00000000FFFFFFFFUL; |
| IMG_UINT32 ui32SegOutAddr1 = (ui64SegOutAddr >> 32) & 0x00000000FFFFFFFFUL; |
| |
| /* META segments have a minimum size */ |
| IMG_UINT32 ui32LimitOff = (ui32SegLimit < RGXFW_SEGMMU_ALIGN) ? |
| RGXFW_SEGMMU_ALIGN : ui32SegLimit; |
| /* the limit is an offset, therefore off = size - 1 */ |
| ui32LimitOff -= 1; |
| |
| RGXCommentLog(hPrivate, |
| "* Seg%d: meta_addr = 0x%08x, devv_addr = 0x%" IMG_UINT64_FMTSPECx ", limit = 0x%x", |
| ui32SegID, |
| ui32SegBase, |
| ui64SegOutAddr, |
| ui32LimitOff); |
| |
| ui32SegBase |= RGXFW_SEGMMU_ALLTHRS_WRITEABLE; |
| |
| *pui32BootConf++ = META_CR_MMCU_SEGMENTn_BASE(ui32SegID); |
| *pui32BootConf++ = ui32SegBase; |
| |
| *pui32BootConf++ = META_CR_MMCU_SEGMENTn_LIMIT(ui32SegID); |
| *pui32BootConf++ = ui32LimitOff; |
| |
| *pui32BootConf++ = META_CR_MMCU_SEGMENTn_OUTA0(ui32SegID); |
| *pui32BootConf++ = ui32SegOutAddr0; |
| |
| *pui32BootConf++ = META_CR_MMCU_SEGMENTn_OUTA1(ui32SegID); |
| *pui32BootConf++ = ui32SegOutAddr1; |
| |
| *ppui32BootConf = pui32BootConf; |
| } |
| |
| /*! |
| ******************************************************************************* |
| |
| @Function RGXFWConfigureSegMMU |
| |
| @Description Configures META's Segment MMU |
| |
| @Input hPrivate : Implementation specific data |
| @Input psFWCodeDevVAddrBase : FW code base device virtual address |
| @Input psFWDataDevVAddrBase : FW data base device virtual address |
| @Input ppui32BootConf : Pointer to bootloader data |
| |
| @Return void |
| |
| ******************************************************************************/ |
| static void RGXFWConfigureSegMMU(const void *hPrivate, |
| IMG_DEV_VIRTADDR *psFWCodeDevVAddrBase, |
| IMG_DEV_VIRTADDR *psFWDataDevVAddrBase, |
| IMG_UINT32 **ppui32BootConf) |
| { |
| IMG_UINT64 ui64SegOutAddrTop; |
| IMG_UINT32 i; |
| |
| PVR_UNREFERENCED_PARAMETER(psFWCodeDevVAddrBase); |
| |
| /* Configure Segment MMU */ |
| RGXCommentLog(hPrivate, "********** FW configure Segment MMU **********"); |
| |
| if (RGX_DEVICE_HAS_ERN(hPrivate, 45914)) |
| { |
| ui64SegOutAddrTop = RGXFW_SEGMMU_OUTADDR_TOP_ERN_45914(META_MMU_CONTEXT_MAPPING, RGXFW_SEGMMU_META_DM_ID); |
| } |
| else |
| { |
| ui64SegOutAddrTop = RGXFW_SEGMMU_OUTADDR_TOP_PRE_S7(META_MMU_CONTEXT_MAPPING, RGXFW_SEGMMU_META_DM_ID); |
| } |
| |
| for (i = 0; i < ui32LayoutEntryNum; i++) |
| { |
| /* |
| * FW code is using the bootloader segment which is already configured on boot. |
| * FW coremem code and data don't use the segment MMU. |
| * Only the FW data segment needs to be configured. |
| */ |
| |
| if (asRGXFWLayoutTable[i].eType == FW_DATA) |
| { |
| IMG_UINT64 ui64SegOutAddr; |
| IMG_UINT32 ui32SegId = RGXFW_SEGMMU_DATA_ID; |
| |
| ui64SegOutAddr = (psFWDataDevVAddrBase->uiAddr | ui64SegOutAddrTop) + |
| asRGXFWLayoutTable[i].ui32AllocOffset; |
| |
| RGXFWConfigureSegID(hPrivate, |
| ui64SegOutAddr, |
| asRGXFWLayoutTable[i].ui32BaseAddr, |
| asRGXFWLayoutTable[i].ui32AllocSize, |
| ui32SegId, |
| ppui32BootConf); /*write the sequence to the bootldr */ |
| |
| break; |
| } |
| } |
| } |
| |
| /*! |
| ******************************************************************************* |
| |
| @Function RGXFWConfigureMetaCaches |
| |
| @Description Configure and enable the Meta instruction and data caches |
| |
| @Input hPrivate : Implementation specific data |
| @Input ui32NumThreads : Number of FW threads in use |
| @Input ui32MainThreadID : ID of the FW thread in use |
| (only meaningful if ui32NumThreads == 1) |
| @Input ppui32BootConf : Pointer to bootloader data |
| |
| @Return void |
| |
| ******************************************************************************/ |
| static void RGXFWConfigureMetaCaches(const void *hPrivate, |
| IMG_UINT32 ui32NumThreads, |
| IMG_UINT32 ui32MainThreadID, |
| IMG_UINT32 **ppui32BootConf) |
| { |
| IMG_UINT32 *pui32BootConf = *ppui32BootConf; |
| IMG_UINT32 ui32DCacheT0, ui32ICacheT0; |
| IMG_UINT32 ui32DCacheT1, ui32ICacheT1; |
| IMG_UINT32 ui32DCacheT2, ui32ICacheT2; |
| IMG_UINT32 ui32DCacheT3, ui32ICacheT3; |
| |
| #define META_CR_MMCU_LOCAL_EBCTRL (0x04830600) |
| #define META_CR_MMCU_LOCAL_EBCTRL_ICWIN (0x3 << 14) |
| #define META_CR_MMCU_LOCAL_EBCTRL_DCWIN (0x3 << 6) |
| #define META_CR_SYSC_DCPART(n) (0x04830200 + (n)*0x8) |
| #define META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE (0x1 << 31) |
| #define META_CR_SYSC_ICPART(n) (0x04830220 + (n)*0x8) |
| #define META_CR_SYSC_XCPARTX_LOCAL_ADDR_OFFSET_TOP_HALF (0x8 << 16) |
| #define META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE (0xF) |
| #define META_CR_SYSC_XCPARTX_LOCAL_ADDR_HALF_CACHE (0x7) |
| #define META_CR_MMCU_DCACHE_CTRL (0x04830018) |
| #define META_CR_MMCU_ICACHE_CTRL (0x04830020) |
| #define META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN (0x1) |
| |
| RGXCommentLog(hPrivate, "********** Meta caches configuration *********"); |
| |
| /* Initialise I/Dcache settings */ |
| ui32DCacheT0 = ui32DCacheT1 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE; |
| ui32DCacheT2 = ui32DCacheT3 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE; |
| ui32ICacheT0 = ui32ICacheT1 = ui32ICacheT2 = ui32ICacheT3 = 0; |
| |
| if (ui32NumThreads == 1) |
| { |
| if (ui32MainThreadID == 0) |
| { |
| ui32DCacheT0 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE; |
| ui32ICacheT0 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE; |
| } |
| else |
| { |
| ui32DCacheT1 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE; |
| ui32ICacheT1 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE; |
| } |
| } |
| else |
| { |
| ui32DCacheT0 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_HALF_CACHE; |
| ui32ICacheT0 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_HALF_CACHE; |
| |
| ui32DCacheT1 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_HALF_CACHE | |
| META_CR_SYSC_XCPARTX_LOCAL_ADDR_OFFSET_TOP_HALF; |
| ui32ICacheT1 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_HALF_CACHE | |
| META_CR_SYSC_XCPARTX_LOCAL_ADDR_OFFSET_TOP_HALF; |
| } |
| |
| /* Local region MMU enhanced bypass: WIN-3 mode for code and data caches */ |
| *pui32BootConf++ = META_CR_MMCU_LOCAL_EBCTRL; |
| *pui32BootConf++ = META_CR_MMCU_LOCAL_EBCTRL_ICWIN | |
| META_CR_MMCU_LOCAL_EBCTRL_DCWIN; |
| |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_MMCU_LOCAL_EBCTRL, |
| META_CR_MMCU_LOCAL_EBCTRL_ICWIN | META_CR_MMCU_LOCAL_EBCTRL_DCWIN); |
| |
| /* Data cache partitioning thread 0 to 3 */ |
| *pui32BootConf++ = META_CR_SYSC_DCPART(0); |
| *pui32BootConf++ = ui32DCacheT0; |
| *pui32BootConf++ = META_CR_SYSC_DCPART(1); |
| *pui32BootConf++ = ui32DCacheT1; |
| *pui32BootConf++ = META_CR_SYSC_DCPART(2); |
| *pui32BootConf++ = ui32DCacheT2; |
| *pui32BootConf++ = META_CR_SYSC_DCPART(3); |
| *pui32BootConf++ = ui32DCacheT3; |
| |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_SYSC_DCPART(0), ui32DCacheT0); |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_SYSC_DCPART(1), ui32DCacheT1); |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_SYSC_DCPART(2), ui32DCacheT2); |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_SYSC_DCPART(3), ui32DCacheT3); |
| |
| /* Enable data cache hits */ |
| *pui32BootConf++ = META_CR_MMCU_DCACHE_CTRL; |
| *pui32BootConf++ = META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN; |
| |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_MMCU_DCACHE_CTRL, |
| META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN); |
| |
| /* Instruction cache partitioning thread 0 to 3 */ |
| *pui32BootConf++ = META_CR_SYSC_ICPART(0); |
| *pui32BootConf++ = ui32ICacheT0; |
| *pui32BootConf++ = META_CR_SYSC_ICPART(1); |
| *pui32BootConf++ = ui32ICacheT1; |
| *pui32BootConf++ = META_CR_SYSC_ICPART(2); |
| *pui32BootConf++ = ui32ICacheT2; |
| *pui32BootConf++ = META_CR_SYSC_ICPART(3); |
| *pui32BootConf++ = ui32ICacheT3; |
| |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_SYSC_ICPART(0), ui32ICacheT0); |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_SYSC_ICPART(1), ui32ICacheT1); |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_SYSC_ICPART(2), ui32ICacheT2); |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_SYSC_ICPART(3), ui32ICacheT3); |
| |
| /* Enable instruction cache hits */ |
| *pui32BootConf++ = META_CR_MMCU_ICACHE_CTRL; |
| *pui32BootConf++ = META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN; |
| |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| META_CR_MMCU_ICACHE_CTRL, |
| META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN); |
| |
| *pui32BootConf++ = 0x040000C0; |
| *pui32BootConf++ = 0; |
| |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", 0x040000C0, 0); |
| |
| *ppui32BootConf = pui32BootConf; |
| } |
| |
| /*! |
| ******************************************************************************* |
| |
| @Function ProcessLDRCommandStream |
| |
| @Description Process the output of the Meta toolchain in the .LDR format |
| copying code and data sections into their final location and |
| passing some information to the Meta bootloader |
| |
| @Input hPrivate : Implementation specific data |
| @Input pbLDR : Pointer to FW blob |
| @Input pvHostFWCodeAddr : Pointer to FW code |
| @Input pvHostFWDataAddr : Pointer to FW data |
| @Input pvHostFWCorememCodeAddr : Pointer to FW coremem code |
| @Input pvHostFWCorememDataAddr : Pointer to FW coremem data |
| @Input ppui32BootConf : Pointer to bootloader data |
| |
| @Return PVRSRV_ERROR |
| |
| ******************************************************************************/ |
| PVRSRV_ERROR ProcessLDRCommandStream(const void *hPrivate, |
| const IMG_BYTE* pbLDR, |
| void* pvHostFWCodeAddr, |
| void* pvHostFWDataAddr, |
| void* pvHostFWCorememCodeAddr, |
| void* pvHostFWCorememDataAddr, |
| IMG_UINT32 **ppui32BootConf) |
| { |
| RGX_META_LDR_BLOCK_HDR *psHeader = (RGX_META_LDR_BLOCK_HDR *) pbLDR; |
| RGX_META_LDR_L1_DATA_BLK *psL1Data = |
| (RGX_META_LDR_L1_DATA_BLK*) ((IMG_UINT8 *) pbLDR + psHeader->ui32SLData); |
| |
| IMG_UINT32 *pui32BootConf = ppui32BootConf ? *ppui32BootConf : NULL; |
| IMG_UINT32 ui32CorememSize = RGXGetFWCorememSize(hPrivate); |
| |
| RGXCommentLog(hPrivate, "**********************************************"); |
| RGXCommentLog(hPrivate, "************** Begin LDR Parsing *************"); |
| RGXCommentLog(hPrivate, "**********************************************"); |
| |
| while (psL1Data != NULL) |
| { |
| if (RGX_META_LDR_BLK_IS_COMMENT(psL1Data->ui16Cmd)) |
| { |
| /* Don't process comment blocks */ |
| goto NextBlock; |
| } |
| |
| switch (psL1Data->ui16Cmd & RGX_META_LDR_CMD_MASK) |
| { |
| case RGX_META_LDR_CMD_LOADMEM: |
| { |
| RGX_META_LDR_L2_DATA_BLK *psL2Block = |
| (RGX_META_LDR_L2_DATA_BLK*) (((IMG_UINT8 *) pbLDR) + psL1Data->aui32CmdData[1]); |
| IMG_UINT32 ui32Offset = psL1Data->aui32CmdData[0]; |
| IMG_UINT32 ui32DataSize = psL2Block->ui16Length - 6 /* L2 Tag length and checksum */; |
| void *pvWriteAddr; |
| PVRSRV_ERROR eError; |
| IMG_BOOL bIsCoremem = IMG_FALSE; |
| IMG_UINT32 ui32CorememStartAddr; |
| void *pvFWCorememAddr = NULL; |
| |
| if (RGX_META_IS_COREMEM_CODE(ui32Offset, ui32CorememSize)) |
| { |
| RGX_FW_LAYOUT_ENTRY *psEntry = GetTableEntry(hPrivate, META_COREMEM_CODE); |
| |
| /* coremem code */ |
| bIsCoremem = IMG_TRUE; |
| ui32CorememStartAddr = psEntry->ui32BaseAddr; |
| pvFWCorememAddr = pvHostFWCorememCodeAddr; |
| } |
| else if (RGX_META_IS_COREMEM_DATA(ui32Offset, ui32CorememSize)) |
| { |
| RGX_FW_LAYOUT_ENTRY *psEntry = GetTableEntry(hPrivate, META_COREMEM_DATA); |
| |
| /* coremem data */ |
| bIsCoremem = IMG_TRUE; |
| ui32CorememStartAddr = psEntry->ui32BaseAddr; |
| pvFWCorememAddr = pvHostFWCorememDataAddr; |
| } |
| |
| if (bIsCoremem) |
| { |
| /* Check that there is a valid allocation for the coremem code/data */ |
| if (pvFWCorememAddr == NULL) |
| { |
| RGXErrorLog(hPrivate, |
| "%s: Coremem code/data found but no coremem allocation available!", |
| __func__); |
| |
| return PVRSRV_ERROR_INIT_FAILURE; |
| } |
| |
| /* Copy coremem data to buffer. The FW copies it to the actual coremem */ |
| ui32Offset -= ui32CorememStartAddr; |
| |
| RGXMemCopy(hPrivate, |
| (void*)((IMG_UINT8 *)pvFWCorememAddr + ui32Offset), |
| psL2Block->aui32BlockData, |
| ui32DataSize); |
| } |
| else |
| { |
| /* Global range is aliased to local range */ |
| ui32Offset &= ~META_MEM_GLOBAL_RANGE_BIT; |
| |
| eError = FindMMUSegment(ui32Offset, |
| pvHostFWCodeAddr, |
| pvHostFWDataAddr, |
| &pvWriteAddr); |
| |
| if (eError != PVRSRV_OK) |
| { |
| RGXErrorLog(hPrivate, |
| "ProcessLDRCommandStream: Addr 0x%x (size: %d) not found in any segment", |
| ui32Offset, ui32DataSize); |
| return eError; |
| } |
| |
| /* Write to FW allocation only if available */ |
| if (pvWriteAddr) |
| { |
| RGXMemCopy(hPrivate, |
| pvWriteAddr, |
| psL2Block->aui32BlockData, |
| ui32DataSize); |
| } |
| } |
| |
| break; |
| } |
| case RGX_META_LDR_CMD_LOADCORE: |
| case RGX_META_LDR_CMD_LOADMMREG: |
| { |
| return PVRSRV_ERROR_INIT_FAILURE; |
| } |
| case RGX_META_LDR_CMD_START_THREADS: |
| { |
| /* Don't process this block */ |
| break; |
| } |
| case RGX_META_LDR_CMD_ZEROMEM: |
| { |
| IMG_UINT32 ui32Offset = psL1Data->aui32CmdData[0]; |
| IMG_UINT32 ui32ByteCount = psL1Data->aui32CmdData[1]; |
| void *pvWriteAddr; |
| PVRSRV_ERROR eError; |
| |
| if (RGX_META_IS_COREMEM_DATA(ui32Offset, ui32CorememSize)) |
| { |
| /* cannot zero coremem directly */ |
| break; |
| } |
| |
| /* Global range is aliased to local range */ |
| ui32Offset &= ~META_MEM_GLOBAL_RANGE_BIT; |
| |
| eError = FindMMUSegment(ui32Offset, |
| pvHostFWCodeAddr, |
| pvHostFWDataAddr, |
| &pvWriteAddr); |
| |
| if (eError != PVRSRV_OK) |
| { |
| RGXErrorLog(hPrivate, |
| "ProcessLDRCommandStream: Addr 0x%x (size: %d) not found in any segment", |
| ui32Offset, ui32ByteCount); |
| return eError; |
| } |
| |
| /* Write to FW allocation only if available */ |
| if (pvWriteAddr) |
| { |
| RGXMemSet(hPrivate, pvWriteAddr, 0, ui32ByteCount); |
| } |
| |
| break; |
| } |
| case RGX_META_LDR_CMD_CONFIG: |
| { |
| RGX_META_LDR_L2_DATA_BLK *psL2Block = |
| (RGX_META_LDR_L2_DATA_BLK*) (((IMG_UINT8 *) pbLDR) + psL1Data->aui32CmdData[0]); |
| RGX_META_LDR_CFG_BLK *psConfigCommand = (RGX_META_LDR_CFG_BLK*) psL2Block->aui32BlockData; |
| IMG_UINT32 ui32L2BlockSize = psL2Block->ui16Length - 6 /* L2 Tag length and checksum */; |
| IMG_UINT32 ui32CurrBlockSize = 0; |
| |
| while (ui32L2BlockSize) |
| { |
| switch (psConfigCommand->ui32Type) |
| { |
| case RGX_META_LDR_CFG_PAUSE: |
| case RGX_META_LDR_CFG_READ: |
| { |
| ui32CurrBlockSize = 8; |
| return PVRSRV_ERROR_INIT_FAILURE; |
| } |
| case RGX_META_LDR_CFG_WRITE: |
| { |
| IMG_UINT32 ui32RegisterOffset = psConfigCommand->aui32BlockData[0]; |
| IMG_UINT32 ui32RegisterValue = psConfigCommand->aui32BlockData[1]; |
| |
| /* Only write to bootloader if we got a valid |
| * pointer to the FW code allocation |
| */ |
| if (pui32BootConf) |
| { |
| /* Do register write */ |
| *pui32BootConf++ = ui32RegisterOffset; |
| *pui32BootConf++ = ui32RegisterValue; |
| } |
| |
| RGXCommentLog(hPrivate, "Meta SP: [0x%08x] = 0x%08x", |
| ui32RegisterOffset, ui32RegisterValue); |
| |
| ui32CurrBlockSize = 12; |
| break; |
| } |
| case RGX_META_LDR_CFG_MEMSET: |
| case RGX_META_LDR_CFG_MEMCHECK: |
| { |
| ui32CurrBlockSize = 20; |
| return PVRSRV_ERROR_INIT_FAILURE; |
| } |
| default: |
| { |
| return PVRSRV_ERROR_INIT_FAILURE; |
| } |
| } |
| ui32L2BlockSize -= ui32CurrBlockSize; |
| psConfigCommand = (RGX_META_LDR_CFG_BLK*) (((IMG_UINT8*) psConfigCommand) + ui32CurrBlockSize); |
| } |
| |
| break; |
| } |
| default: |
| { |
| return PVRSRV_ERROR_INIT_FAILURE; |
| } |
| } |
| |
| NextBlock: |
| |
| if (psL1Data->ui32Next == 0xFFFFFFFF) |
| { |
| psL1Data = NULL; |
| } |
| else |
| { |
| psL1Data = (RGX_META_LDR_L1_DATA_BLK*) (((IMG_UINT8 *) pbLDR) + psL1Data->ui32Next); |
| } |
| } |
| |
| if (pui32BootConf) |
| { |
| *ppui32BootConf = pui32BootConf; |
| } |
| |
| RGXCommentLog(hPrivate, "**********************************************"); |
| RGXCommentLog(hPrivate, "************** End Loader Parsing ************"); |
| RGXCommentLog(hPrivate, "**********************************************"); |
| |
| return PVRSRV_OK; |
| } |
| |
| /*! |
| ******************************************************************************* |
| |
| @Function ProcessELFCommandStream |
| |
| @Description Process the output of the Mips toolchain in the .ELF format |
| copying code and data sections into their final location |
| |
| @Input hPrivate : Implementation specific data |
| @Input pbELF : Pointer to FW blob |
| @Input pvHostFWCodeAddr : Pointer to FW code |
| @Input pvHostFWDataAddr : Pointer to FW data |
| |
| @Return PVRSRV_ERROR |
| |
| ******************************************************************************/ |
| PVRSRV_ERROR ProcessELFCommandStream(const void *hPrivate, |
| const IMG_BYTE *pbELF, |
| void *pvHostFWCodeAddr, |
| void *pvHostFWDataAddr) |
| { |
| IMG_UINT32 ui32Entry; |
| RGX_MIPS_ELF_HDR *psHeader = (RGX_MIPS_ELF_HDR *)pbELF; |
| RGX_MIPS_ELF_PROGRAM_HDR *psProgramHeader = |
| (RGX_MIPS_ELF_PROGRAM_HDR *)(pbELF + psHeader->ui32Ephoff); |
| PVRSRV_ERROR eError; |
| |
| for (ui32Entry = 0; ui32Entry < psHeader->ui32Ephnum; ui32Entry++, psProgramHeader++) |
| { |
| void *pvWriteAddr; |
| |
| /* Only consider loadable entries in the ELF segment table */ |
| if (psProgramHeader->ui32Ptype != ELF_PT_LOAD) continue; |
| |
| eError = FindMMUSegment(psProgramHeader->ui32Pvaddr, |
| pvHostFWCodeAddr, |
| pvHostFWDataAddr, |
| &pvWriteAddr); |
| |
| if (eError != PVRSRV_OK) |
| { |
| RGXErrorLog(hPrivate, |
| "%s: Addr 0x%x (size: %d) not found in any segment",__func__, |
| psProgramHeader->ui32Pvaddr, |
| psProgramHeader->ui32Pfilesz); |
| return eError; |
| } |
| |
| /* Write to FW allocation only if available */ |
| if (pvWriteAddr) |
| { |
| RGXMemCopy(hPrivate, |
| pvWriteAddr, |
| (IMG_PBYTE)(pbELF + psProgramHeader->ui32Poffset), |
| psProgramHeader->ui32Pfilesz); |
| |
| RGXMemSet(hPrivate, |
| (IMG_PBYTE)pvWriteAddr + psProgramHeader->ui32Pfilesz, |
| 0, |
| psProgramHeader->ui32Pmemsz - psProgramHeader->ui32Pfilesz); |
| } |
| } |
| |
| return PVRSRV_OK; |
| } |
| |
| IMG_UINT32 RGXGetFWImageSectionOffset(const void *hPrivate, RGX_FW_SECTION_ID eId) |
| { |
| RGX_FW_LAYOUT_ENTRY *psEntry = GetTableEntry(hPrivate, eId); |
| |
| return psEntry->ui32AllocOffset; |
| } |
| |
| IMG_UINT32 RGXGetFWImageSectionMaxSize(const void *hPrivate, RGX_FW_SECTION_ID eId) |
| { |
| RGX_FW_LAYOUT_ENTRY *psEntry = GetTableEntry(hPrivate, eId); |
| |
| return psEntry->ui32MaxSize; |
| } |
| |
| IMG_UINT32 RGXGetFWImageSectionAllocSize(const void *hPrivate, RGX_FW_SECTION_ID eId) |
| { |
| RGX_FW_LAYOUT_ENTRY *psEntry = GetTableEntry(hPrivate, eId); |
| |
| return psEntry->ui32AllocSize; |
| } |
| |
| IMG_UINT32 RGXGetFWImageSectionAddress(const void *hPrivate, RGX_FW_SECTION_ID eId) |
| { |
| RGX_FW_LAYOUT_ENTRY *psEntry = GetTableEntry(hPrivate, eId); |
| |
| return psEntry->ui32BaseAddr; |
| } |
| |
| PVRSRV_ERROR RGXGetFWImageAllocSize(const void *hPrivate, |
| const IMG_BYTE *pbRGXFirmware, |
| const IMG_UINT32 ui32RGXFirmwareSize, |
| IMG_DEVMEM_SIZE_T *puiFWCodeAllocSize, |
| IMG_DEVMEM_SIZE_T *puiFWDataAllocSize, |
| IMG_DEVMEM_SIZE_T *puiFWCorememCodeAllocSize, |
| IMG_DEVMEM_SIZE_T *puiFWCorememDataAllocSize) |
| { |
| RGX_FW_INFO_HEADER *psInfoHeader; |
| const IMG_BYTE *pbRGXFirmwareInfo; |
| const IMG_BYTE *pbRGXFirmwareLayout; |
| IMG_UINT32 i; |
| |
| if (pbRGXFirmware == NULL || ui32RGXFirmwareSize == 0 || ui32RGXFirmwareSize <= FW_BLOCK_SIZE) |
| { |
| RGXErrorLog(hPrivate, "%s: Invalid FW binary at %p, size %u", |
| __func__, pbRGXFirmware, ui32RGXFirmwareSize); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| |
| /* |
| * Acquire pointer to the FW info header within the FW image. |
| * The format of the header in the FW image might not be the one expected |
| * by the driver, but the driver should still be able to correctly read |
| * the information below, as long as new/incompatible elements are added |
| * at the end of the header (they will be ignored by the driver). |
| */ |
| |
| pbRGXFirmwareInfo = pbRGXFirmware + ui32RGXFirmwareSize - FW_BLOCK_SIZE; |
| psInfoHeader = (RGX_FW_INFO_HEADER*)pbRGXFirmwareInfo; |
| |
| /* If any of the following checks fails, the FW will likely not work properly */ |
| |
| if (psInfoHeader->ui32InfoVersion != FW_INFO_VERSION) |
| { |
| RGXErrorLog(hPrivate, "%s: FW info version mismatch (expected: %u, found: %u)", |
| __func__, |
| (IMG_UINT32) FW_INFO_VERSION, |
| psInfoHeader->ui32InfoVersion); |
| } |
| |
| if (psInfoHeader->ui32HeaderLen != sizeof(RGX_FW_INFO_HEADER)) |
| { |
| RGXErrorLog(hPrivate, "%s: FW info header sizes mismatch (expected: %u, found: %u)", |
| __func__, |
| (IMG_UINT32) sizeof(RGX_FW_INFO_HEADER), |
| psInfoHeader->ui32HeaderLen); |
| } |
| |
| if (psInfoHeader->ui32LayoutEntrySize != sizeof(RGX_FW_LAYOUT_ENTRY)) |
| { |
| RGXErrorLog(hPrivate, "%s: FW layout entry sizes mismatch (expected: %u, found: %u)", |
| __func__, |
| (IMG_UINT32) sizeof(RGX_FW_LAYOUT_ENTRY), |
| psInfoHeader->ui32LayoutEntrySize); |
| } |
| |
| if (psInfoHeader->ui32LayoutEntryNum > MAX_NUM_ENTRIES) |
| { |
| RGXErrorLog(hPrivate, "%s: Not enough storage for the FW layout table (max: %u entries, found: %u)", |
| __func__, |
| MAX_NUM_ENTRIES, |
| psInfoHeader->ui32LayoutEntryNum); |
| } |
| |
| ui32LayoutEntryNum = psInfoHeader->ui32LayoutEntryNum; |
| |
| |
| /* |
| * Copy FW layout table from FW image to local array. |
| * One entry is copied at a time and the copy is limited to what the driver |
| * expects to find in it. Assuming that new/incompatible elements |
| * are added at the end of each entry, the loop below adapts the table |
| * in the FW image into the format expected by the driver. |
| */ |
| |
| pbRGXFirmwareLayout = pbRGXFirmwareInfo + psInfoHeader->ui32HeaderLen; |
| |
| for (i = 0; i < ui32LayoutEntryNum; i++) |
| { |
| RGX_FW_LAYOUT_ENTRY *psOutEntry = &asRGXFWLayoutTable[i]; |
| |
| RGX_FW_LAYOUT_ENTRY *psInEntry = (RGX_FW_LAYOUT_ENTRY*) |
| (pbRGXFirmwareLayout + i * psInfoHeader->ui32LayoutEntrySize); |
| |
| RGXMemCopy(hPrivate, |
| (void*)psOutEntry, |
| (void*)psInEntry, |
| sizeof(RGX_FW_LAYOUT_ENTRY)); |
| } |
| |
| |
| /* Calculate how much memory the FW needs for its code and data segments */ |
| |
| *puiFWCodeAllocSize = 0; |
| *puiFWDataAllocSize = 0; |
| *puiFWCorememCodeAllocSize = 0; |
| *puiFWCorememDataAllocSize = 0; |
| |
| for (i = 0; i < ui32LayoutEntryNum; i++) |
| { |
| switch (asRGXFWLayoutTable[i].eType) |
| { |
| case FW_CODE: |
| *puiFWCodeAllocSize += asRGXFWLayoutTable[i].ui32AllocSize; |
| break; |
| |
| case FW_DATA: |
| *puiFWDataAllocSize += asRGXFWLayoutTable[i].ui32AllocSize; |
| break; |
| |
| case FW_COREMEM_CODE: |
| *puiFWCorememCodeAllocSize += asRGXFWLayoutTable[i].ui32AllocSize; |
| break; |
| |
| case FW_COREMEM_DATA: |
| *puiFWCorememDataAllocSize += asRGXFWLayoutTable[i].ui32AllocSize; |
| break; |
| |
| default: |
| RGXErrorLog(hPrivate, "%s: Unknown FW section type %u\n", |
| __func__, asRGXFWLayoutTable[i].eType); |
| break; |
| } |
| } |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| PVRSRV_ERROR RGXProcessFWImage(const void *hPrivate, |
| const IMG_BYTE *pbRGXFirmware, |
| void *pvFWCode, |
| void *pvFWData, |
| void *pvFWCorememCode, |
| void *pvFWCorememData, |
| RGX_FW_BOOT_PARAMS *puFWParams) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| IMG_BOOL bMIPS = RGX_DEVICE_HAS_FEATURE(hPrivate, MIPS); |
| |
| if (!bMIPS) |
| { |
| IMG_UINT32 *pui32BootConf = NULL; |
| /* Skip bootloader configuration if a pointer to the FW code |
| * allocation is not available |
| */ |
| if (pvFWCode) |
| { |
| /* This variable points to the bootloader code which is mostly |
| * a sequence of <register address,register value> pairs |
| */ |
| pui32BootConf = ((IMG_UINT32*) pvFWCode) + RGXFW_BOOTLDR_CONF_OFFSET; |
| |
| /* Slave port and JTAG accesses are privileged */ |
| *pui32BootConf++ = META_CR_SYSC_JTAG_THREAD; |
| *pui32BootConf++ = META_CR_SYSC_JTAG_THREAD_PRIV_EN; |
| |
| RGXFWConfigureSegMMU(hPrivate, |
| &puFWParams->sMeta.sFWCodeDevVAddr, |
| &puFWParams->sMeta.sFWDataDevVAddr, |
| &pui32BootConf); |
| } |
| |
| /* Process FW image data stream */ |
| eError = ProcessLDRCommandStream(hPrivate, |
| pbRGXFirmware, |
| pvFWCode, |
| pvFWData, |
| pvFWCorememCode, |
| pvFWCorememData, |
| &pui32BootConf); |
| if (eError != PVRSRV_OK) |
| { |
| RGXErrorLog(hPrivate, "RGXProcessFWImage: Processing FW image failed (%d)", eError); |
| return eError; |
| } |
| |
| /* Skip bootloader configuration if a pointer to the FW code |
| * allocation is not available |
| */ |
| if (pvFWCode) |
| { |
| IMG_UINT32 ui32MainThreadID = puFWParams->sMeta.ui32MainThreadID; |
| IMG_UINT32 ui32NumThreads = puFWParams->sMeta.ui32NumThreads; |
| |
| if ((ui32NumThreads == 0) || (ui32NumThreads > 2) || (ui32MainThreadID >= 2)) |
| { |
| RGXErrorLog(hPrivate, |
| "ProcessFWImage: Wrong Meta threads configuration, using one thread only"); |
| |
| ui32NumThreads = 1; |
| ui32MainThreadID = 0; |
| } |
| |
| RGXFWConfigureMetaCaches(hPrivate, |
| ui32NumThreads, |
| ui32MainThreadID, |
| &pui32BootConf); |
| |
| /* Signal the end of the conf sequence */ |
| *pui32BootConf++ = 0x0; |
| *pui32BootConf++ = 0x0; |
| |
| if (puFWParams->sMeta.uiFWCorememCodeSize && (puFWParams->sMeta.sFWCorememCodeFWAddr.ui32Addr != 0)) |
| { |
| *pui32BootConf++ = puFWParams->sMeta.sFWCorememCodeFWAddr.ui32Addr; |
| *pui32BootConf++ = puFWParams->sMeta.uiFWCorememCodeSize; |
| } |
| else |
| { |
| *pui32BootConf++ = 0; |
| *pui32BootConf++ = 0; |
| } |
| |
| if (RGX_DEVICE_HAS_FEATURE(hPrivate, META_DMA)) |
| { |
| *pui32BootConf++ = (IMG_UINT32) (puFWParams->sMeta.sFWCorememCodeDevVAddr.uiAddr >> 32); |
| *pui32BootConf++ = (IMG_UINT32) puFWParams->sMeta.sFWCorememCodeDevVAddr.uiAddr; |
| } |
| else |
| { |
| *pui32BootConf++ = 0; |
| *pui32BootConf++ = 0; |
| } |
| |
| } |
| } |
| |
| if (bMIPS) |
| { |
| /* Process FW image data stream */ |
| eError = ProcessELFCommandStream(hPrivate, |
| pbRGXFirmware, |
| pvFWCode, |
| pvFWData); |
| if (eError != PVRSRV_OK) |
| { |
| RGXErrorLog(hPrivate, "RGXProcessFWImage: Processing FW image failed (%d)", eError); |
| return eError; |
| } |
| |
| if (pvFWData) |
| { |
| /* To get a pointer to the bootloader configuration data start from a pointer to the FW image... */ |
| IMG_UINT64 *pui64BootConfig = (IMG_UINT64 *) pvFWData; |
| |
| /* ... jump to the boot/NMI data page... */ |
| pui64BootConfig += RGXMIPSFW_GET_OFFSET_IN_QWORDS(RGXGetFWImageSectionOffset(NULL, MIPS_BOOT_DATA)); |
| |
| /* ... and then jump to the bootloader data offset within the page */ |
| pui64BootConfig += RGXMIPSFW_GET_OFFSET_IN_QWORDS(RGXMIPSFW_BOOTLDR_CONF_OFFSET); |
| |
| /* Rogue Registers physical address */ |
| pui64BootConfig[RGXMIPSFW_ROGUE_REGS_BASE_PHYADDR_OFFSET] = puFWParams->sMips.sGPURegAddr.uiAddr; |
| |
| /* MIPS Page Table physical address. There are 16 pages for a firmware heap of 32 MB */ |
| pui64BootConfig[RGXMIPSFW_PAGE_TABLE_BASE_PHYADDR_OFFSET] = puFWParams->sMips.sFWPageTableAddr.uiAddr; |
| |
| /* MIPS Stack Pointer Physical Address */ |
| pui64BootConfig[RGXMIPSFW_STACKPOINTER_PHYADDR_OFFSET] = puFWParams->sMips.sFWStackAddr.uiAddr; |
| |
| /* Reserved for future use */ |
| pui64BootConfig[RGXMIPSFW_RESERVED_FUTURE_OFFSET] = 0; |
| } |
| } |
| |
| return eError; |
| } |