1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2019-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "bios_nvswitch.h"
25 #include "error_nvswitch.h"
26 #include "rmsoecmdif.h"
27 #include "nvswitch/lr10/dev_ext_devices.h"
28 
29 #include "flcn/flcn_nvswitch.h"
30 
31 #include "rmflcncmdif_nvswitch.h"
32 #include "haldef_nvswitch.h"
33 
34 static NvlStatus
_nvswitch_core_bios_read(nvswitch_device * device,NvU8 readType,NvU32 reqSize,NvU8 * pData)35 _nvswitch_core_bios_read
36 (
37     nvswitch_device *device,
38     NvU8 readType,
39     NvU32 reqSize,
40     NvU8 *pData
41 )
42 {
43 #define MAX_READ_SIZE 0x2000
44     RM_FLCN_CMD_SOE     cmd;
45     NVSWITCH_TIMEOUT    timeout;
46     NvU32               cmdSeqDesc = 0;
47     NV_STATUS           status;
48     FLCN               *pFlcn = NULL;
49     RM_SOE_CORE_CMD_BIOS *pParams = &cmd.cmd.core.bios;
50     NvU64 dmaHandle = 0;
51     NvU8 *pReadBuffer = NULL;
52     NvU32 offset = 0;
53     NvU32 bufferSize = (reqSize < SOE_DMA_MAX_SIZE) ? SOE_DMA_MAX_SIZE : MAX_READ_SIZE;
54 
55     // Create DMA mapping for SOE CORE transactions
56     status = nvswitch_os_alloc_contig_memory(device->os_handle,
57                  (void**)&pReadBuffer, bufferSize, (device->dma_addr_width == 32));
58     if (status != NVL_SUCCESS)
59     {
60         NVSWITCH_PRINT(device, ERROR, "Failed to allocate contig memory\n");
61         return status;
62     }
63 
64     status = nvswitch_os_map_dma_region(device->os_handle,
65                                         pReadBuffer,
66                                         &dmaHandle,
67                                         bufferSize,
68                                         NVSWITCH_DMA_DIR_TO_SYSMEM);
69     if (status != NVL_SUCCESS)
70     {
71         NVSWITCH_PRINT(device, ERROR, "Failed to map dma region to sysmem\n");
72         nvswitch_os_free_contig_memory(device->os_handle, pReadBuffer, bufferSize);
73         return status;
74     }
75 
76     pFlcn = device->pSoe->pFlcn;
77 
78     while (offset < reqSize)
79     {
80         nvswitch_os_memset(pReadBuffer, 0, bufferSize);
81         nvswitch_os_memset(&cmd, 0, sizeof(cmd));
82 
83         cmd.hdr.unitId = RM_SOE_UNIT_CORE;
84         cmd.hdr.size   = RM_SOE_CMD_SIZE(CORE, BIOS);
85         cmd.cmd.core.bios.cmdType = readType;
86         RM_FLCN_U64_PACK(&pParams->dmaHandle, &dmaHandle);
87         pParams->offset = offset;
88         pParams->sizeInBytes = NV_MIN((reqSize - offset), MAX_READ_SIZE);
89         cmdSeqDesc = 0;
90 
91         status = nvswitch_os_sync_dma_region_for_device(device->os_handle, dmaHandle,
92                         bufferSize, NVSWITCH_DMA_DIR_TO_SYSMEM);
93         if (status != NV_OK)
94         {
95             nvswitch_os_unmap_dma_region(device->os_handle, pReadBuffer, dmaHandle,
96                     bufferSize, NVSWITCH_DMA_DIR_TO_SYSMEM);
97             nvswitch_os_free_contig_memory(device->os_handle, pReadBuffer, bufferSize);
98             NVSWITCH_PRINT(device, ERROR, "Failed to yield to DMA controller\n");
99             return status;
100         }
101 
102         nvswitch_timeout_create(NVSWITCH_INTERVAL_1SEC_IN_NS * 4, &timeout);
103 
104         status = flcnQueueCmdPostBlocking(device, pFlcn,
105                                     (PRM_FLCN_CMD)&cmd,
106                                     NULL,   // pMsg             - not used for now
107                                     NULL,   // pPayload         - not used for now
108                                     SOE_RM_CMDQ_LOG_ID,
109                                     &cmdSeqDesc,
110                                     &timeout);
111         if (status != NV_OK)
112         {
113             NVSWITCH_PRINT(device, ERROR, "%s: CORE read failed. rc:%d\n",
114                             __FUNCTION__, status);
115             break;
116         }
117 
118         status = nvswitch_os_sync_dma_region_for_cpu(device->os_handle, dmaHandle,
119                             bufferSize, NVSWITCH_DMA_DIR_TO_SYSMEM);
120         if (status != NV_OK)
121         {
122             NVSWITCH_PRINT(device, ERROR, "DMA controller failed to yield back\n");
123             break;
124         }
125 
126         if (readType == RM_SOE_CORE_CMD_READ_BIOS)
127         {
128             nvswitch_os_memcpy(((NvU8*)&pData[offset]), pReadBuffer, pParams->sizeInBytes);
129         }
130         else if (readType == RM_SOE_CORE_CMD_READ_BIOS_SIZE)
131         {
132             nvswitch_os_memcpy(((NvU8*)pData), pReadBuffer, reqSize);
133             break;
134         }
135 
136         offset += pParams->sizeInBytes;
137     }
138 
139     nvswitch_os_unmap_dma_region(device->os_handle, pReadBuffer, dmaHandle,
140         bufferSize, NVSWITCH_DMA_DIR_TO_SYSMEM);
141 
142     nvswitch_os_free_contig_memory(device->os_handle, pReadBuffer, bufferSize);
143     return status;
144 }
145 
146 NvlStatus
nvswitch_bios_read(nvswitch_device * device,NvU32 size,void * pData)147 nvswitch_bios_read
148 (
149     nvswitch_device *device,
150     NvU32 size,
151     void *pData
152 )
153 {
154     return _nvswitch_core_bios_read(device, RM_SOE_CORE_CMD_READ_BIOS, size, (NvU8*)pData);
155 }
156 
157 NvlStatus
nvswitch_bios_read_size(nvswitch_device * device,NvU32 * pSize)158 nvswitch_bios_read_size
159 (
160     nvswitch_device *device,
161     NvU32 *pSize
162 )
163 {
164     if (pSize == NULL)
165     {
166         return -NVL_BAD_ARGS;
167     }
168 
169     return _nvswitch_core_bios_read(device, RM_SOE_CORE_CMD_READ_BIOS_SIZE, sizeof(NvU32), (NvU8*)pSize);
170 }
171 
172 /*!
173  * @brief Retrieves BIOS Image via SOE's CORE task
174  * This function needs SOE to be initialized for the Util task to respond.
175  * Upon success the BIOS Image will be place in device.biosImage
176  * @param[in/out] device - pointer to the nvswitch device.
177  */
178 NvlStatus
nvswitch_bios_get_image(nvswitch_device * device)179 nvswitch_bios_get_image
180 (
181     nvswitch_device *device
182 )
183 {
184     NvU8 *pBiosRawBuffer = NULL;
185     NvlStatus status = NVL_SUCCESS;
186     NvU32 biosSize = 0;
187 
188     if (device->biosImage.pImage != NULL)
189     {
190         NVSWITCH_PRINT(device, ERROR,
191                     "NVRM: %s: bios already available, skip reading"
192                     "\n", __FUNCTION__);
193 
194         return NVL_SUCCESS;
195     }
196 
197     if (!device->pSoe)
198     {
199         NVSWITCH_PRINT(device, ERROR,
200             "%s: SOE not initialized yet. \n",
201                 __FUNCTION__);
202         return NVL_SUCCESS;
203     }
204 
205     status = device->hal.nvswitch_get_bios_size(device, &biosSize);
206     if (status != NVL_SUCCESS || biosSize == 0)
207     {
208         NVSWITCH_PRINT(device, ERROR,
209                     "NVRM: %s: bios read size failed"
210                     "\n", __FUNCTION__);
211         return status;
212     }
213 
214     NVSWITCH_PRINT(device, SETUP,
215                     "NVRM: %s: BIOS Size = 0x%x"
216                     "\n", __FUNCTION__, biosSize);
217 
218     pBiosRawBuffer = (NvU8*) nvswitch_os_malloc(biosSize);
219     if (pBiosRawBuffer == NULL)
220     {
221             NVSWITCH_PRINT(device, ERROR,
222                     "%s : failed memory allocation"
223                     "\n", __FUNCTION__);
224 
225         return -NVL_NO_MEM;
226     }
227 
228     nvswitch_os_memset(pBiosRawBuffer, 0, biosSize);
229 
230     status = nvswitch_bios_read(device, biosSize, pBiosRawBuffer);
231     if (status != NVL_SUCCESS)
232     {
233         NVSWITCH_PRINT(device, ERROR, " Failed to retrieve BIOS image, Code 0x%x\n", status);
234         nvswitch_os_free(pBiosRawBuffer);
235         return status;
236     }
237 
238     device->biosImage.pImage = pBiosRawBuffer;
239     device->biosImage.size   = biosSize;
240 
241     return NVL_SUCCESS;
242 }
243