VirtualBox

source: vbox/trunk/src/VBox/Devices/Misc/DevFlashCFI.cpp@ 99766

Last change on this file since 99766 was 99542, checked in by vboxsync, 20 months ago

Devices/DevFlashCFI: Barebones emulation to make the UEFI happy enough to continue, no write support right now, bugref:10434

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.5 KB
Line 
1/* $Id: DevFlashCFI.cpp 99542 2023-04-27 08:03:32Z vboxsync $ */
2/** @file
3 * DevFlashCFI - A simple Flash device implementing the Common Flash Interface
4 * using the sepc from https://ia803103.us.archive.org/30/items/m30l0r7000t0/m30l0r7000t0.pdf
5 *
6 * Implemented as an MMIO device attached directly to the CPU, not behind any
7 * bus. Typically mapped as part of the firmware image.
8 */
9
10/*
11 * Copyright (C) 2023 Oracle and/or its affiliates.
12 *
13 * This file is part of VirtualBox base platform packages, as
14 * available from https://www.virtualbox.org.
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation, in version 3 of the
19 * License.
20 *
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, see <https://www.gnu.org/licenses>.
28 *
29 * SPDX-License-Identifier: GPL-3.0-only
30 */
31
32
33/*********************************************************************************************************************************
34* Header Files *
35*********************************************************************************************************************************/
36#define LOG_GROUP LOG_GROUP_DEV_FLASH
37#include <VBox/vmm/pdmdev.h>
38#include <VBox/log.h>
39#include <VBox/err.h>
40#include <iprt/assert.h>
41#include <iprt/string.h>
42#include <iprt/file.h>
43
44#include "VBoxDD.h"
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50/** @name CFI (Command User Interface) Commands.
51 * @{ */
52/** Block erase setup */
53#define FLASH_CFI_CMD_BLOCK_ERASE_SETUP 0x20
54/** Clear status register. */
55#define FLASH_CFI_CMD_CLEAR_STATUS_REG 0x50
56/** Read status register. */
57#define FLASH_CFI_CMD_READ_STATUS_REG 0x70
58/** Read status register. */
59#define FLASH_CFI_CMD_READ_DEVICE_ID 0x90
60/** Buffered program setup. */
61#define FLASH_CFI_CMD_BUFFERED_PROGRAM_SETUP 0xe8
62/** Buffered program setup. */
63#define FLASH_CFI_CMD_BUFFERED_PROGRAM_CONFIRM 0xd0
64/** Read from the flash array. */
65#define FLASH_CFI_CMD_ARRAY_READ 0xff
66/** @} */
67
68/** @name Status register bits.
69 * @{ */
70/** Bank Write/Multiple Word Program Status Bit. */
71#define FLASH_CFI_SR_BWS RT_BIT(0)
72/** Block Protection Status Bit. */
73#define FLASH_CFI_SR_BLOCK_PROTETION RT_BIT(1)
74#define FLASH_CFI_SR_PROGRAM_SUSPEND RT_BIT(2)
75#define FLASH_CFI_SR_VPP RT_BIT(3)
76#define FLASH_CFI_SR_PROGRAM RT_BIT(4)
77#define FLASH_CFI_SR_ERASE RT_BIT(5)
78#define FLASH_CFI_SR_ERASE_SUSPEND RT_BIT(6)
79#define FLASH_CFI_SR_PROGRAM_ERASE RT_BIT(7)
80/** @} */
81
82
83/*********************************************************************************************************************************
84* Structures and Typedefs *
85*********************************************************************************************************************************/
86/**
87 * The flash device, shared state.
88 */
89typedef struct DEVFLASHCFI
90{
91 /** The current command. */
92 uint16_t u16Cmd;
93 /** The status register. */
94 uint8_t bStatus;
95 /** Current bus cycle. */
96 uint8_t cBusCycle;
97
98 uint8_t cWordsTransfered;
99
100 /** @name The following state does not change at runtime
101 * @{ */
102 /** When set, indicates the state was saved. */
103 bool fStateSaved;
104 /** Manufacturer (high byte) and device (low byte) ID. */
105 uint16_t u16FlashId;
106 /** The configured block size of the device. */
107 uint16_t cbBlockSize;
108 /** The actual flash memory data. */
109 R3PTRTYPE(uint8_t *) pbFlash;
110 /** The flash memory region size. */
111 uint32_t cbFlashSize;
112 /** @} */
113
114 /** The file conaining the flash content. */
115 char *pszFlashFile;
116 /** The guest physical memory base address. */
117 RTGCPHYS GCPhysFlashBase;
118 /** The handle to the MMIO region. */
119 IOMMMIOHANDLE hMmio;
120} DEVFLASHCFI;
121/** Pointer to the Flash device state. */
122typedef DEVFLASHCFI *PDEVFLASHCFI;
123
124
125#ifndef VBOX_DEVICE_STRUCT_TESTCASE
126
127
128/**
129 * @callback_method_impl{FNIOMMMIONEWWRITE, Flash memory write}
130 */
131static DECLCALLBACK(VBOXSTRICTRC) flashMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
132{
133 PDEVFLASHCFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVFLASHCFI);
134 RT_NOREF1(pvUser);
135
136 /** @todo For now we only emulate a x32 device using two x16 chips so the command must be send to both chips at the same time
137 * (and we don't support sending different commands to both devices). */
138 Assert(cb == sizeof(uint32_t));
139 uint32_t u32Val = *(const uint32_t *)pv;
140
141 LogFlowFunc(("off=%RGp u32Val=%#x cb=%u\n", off, u32Val, cb));
142
143 if (pThis->cBusCycle == 0)
144 {
145 /* Writes new command, the comand must be sent to both chips at the same time. */
146 Assert((u32Val >> 16) == (u32Val & 0xffff));
147 switch (u32Val & 0xffff)
148 {
149 case FLASH_CFI_CMD_READ_STATUS_REG:
150 case FLASH_CFI_CMD_ARRAY_READ:
151 case FLASH_CFI_CMD_READ_DEVICE_ID:
152 pThis->u16Cmd = u32Val;
153 break;
154 case FLASH_CFI_CMD_BLOCK_ERASE_SETUP:
155 pThis->u16Cmd = u32Val;
156 pThis->cBusCycle++;
157 break;
158 case FLASH_CFI_CMD_BUFFERED_PROGRAM_SETUP:
159 Assert(pThis->bStatus & FLASH_CFI_SR_PROGRAM_ERASE);
160 pThis->u16Cmd = u32Val;
161 pThis->cBusCycle++;
162 break;
163 case FLASH_CFI_CMD_CLEAR_STATUS_REG:
164 pThis->bStatus &= ~(FLASH_CFI_SR_BLOCK_PROTETION | FLASH_CFI_SR_VPP | FLASH_CFI_SR_PROGRAM | FLASH_CFI_SR_ERASE);
165 pThis->u16Cmd = FLASH_CFI_CMD_ARRAY_READ;
166 break;
167 default:
168 AssertReleaseFailed();
169 }
170 }
171 else if (pThis->cBusCycle == 1)
172 {
173 switch (pThis->u16Cmd)
174 {
175 case FLASH_CFI_CMD_BLOCK_ERASE_SETUP:
176 {
177 /* This contains the address. */
178 pThis->u16Cmd = FLASH_CFI_CMD_READ_STATUS_REG;
179 pThis->cBusCycle = 0;
180 break;
181 }
182 case FLASH_CFI_CMD_BUFFERED_PROGRAM_SETUP:
183 {
184 /* Receives the number of words to be transfered. */
185 pThis->cWordsTransfered = u32Val & 0xffff;
186 pThis->cBusCycle++;
187 break;
188 }
189 }
190 }
191 else if (pThis->cBusCycle == 2)
192 {
193 switch (pThis->u16Cmd)
194 {
195 case FLASH_CFI_CMD_BUFFERED_PROGRAM_SETUP:
196 {
197 /* Receives the address and data. */
198 if (!pThis->cWordsTransfered)
199 {
200 /* Should be the confirm now. */
201 if ((u32Val & 0xffff) == FLASH_CFI_CMD_BUFFERED_PROGRAM_CONFIRM)
202 {
203 /** @todo Write out data to flash. */
204 /* Reset to read array. */
205 pThis->cBusCycle = 0;
206 pThis->u16Cmd = FLASH_CFI_CMD_ARRAY_READ;
207 }
208 }
209 else
210 pThis->cWordsTransfered--;
211 break;
212 }
213 }
214 }
215 else
216 AssertReleaseFailed();
217
218 LogFlow(("flashWrite: completed write at %08RX32 (LB %u)\n", off, cb));
219 return VINF_SUCCESS;
220}
221
222
223/**
224 * @callback_method_impl{FNIOMMMIONEWREAD, Flash memory read}
225 */
226static DECLCALLBACK(VBOXSTRICTRC) flashMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
227{
228 PDEVFLASHCFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVFLASHCFI);
229 RT_NOREF1(pvUser);
230
231 LogFlowFunc(("off=%RGp cb=%u\n", off, cb));
232
233 if (pThis->u16Cmd == FLASH_CFI_CMD_ARRAY_READ)
234 {
235 size_t cbThisRead = RT_MIN(cb, pThis->cbFlashSize - off);
236 size_t cbSetFf = cb - cbThisRead;
237 if (cbThisRead)
238 memcpy(pv, &pThis->pbFlash[off], cbThisRead);
239 if (cbSetFf)
240 memset((uint8_t *)pv + cbThisRead, 0xff, cbSetFf);
241 }
242 else
243 {
244 /** @todo For now we only emulate a x32 device using two x16 chips so the command must be send to both chips at the same time. */
245 Assert(cb == sizeof(uint32_t));
246
247 uint32_t *pu32 = (uint32_t *)pv;
248 switch (pThis->u16Cmd)
249 {
250 case FLASH_CFI_CMD_READ_DEVICE_ID:
251 *pu32 = 0; /** @todo */
252 break;
253 case FLASH_CFI_CMD_READ_STATUS_REG:
254 case FLASH_CFI_CMD_BUFFERED_PROGRAM_SETUP:
255 *pu32 = ((uint32_t)pThis->bStatus << 16) | pThis->bStatus;
256 break;
257 default:
258 AssertReleaseFailed();
259 }
260 }
261
262 LogFlow(("flashRead: completed read at %08RX32 (LB %u)\n", off, cb));
263 return VINF_SUCCESS;
264}
265
266
267#if 0 /** @todo Later */
268/**
269 * @callback_method_impl{FNSSMDEVSAVEEXEC}
270 */
271static DECLCALLBACK(int) flashSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
272{
273 PDEVFLASH pThis = PDMDEVINS_2_DATA(pDevIns, PDEVFLASH);
274 return flashR3SaveExec(&pThis->Core, pDevIns, pSSM);
275}
276
277
278/**
279 * @callback_method_impl{FNSSMDEVLOADEXEC}
280 */
281static DECLCALLBACK(int) flashLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
282{
283 PDEVFLASH pThis = PDMDEVINS_2_DATA(pDevIns, PDEVFLASH);
284 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
285
286 /* Fend off unsupported versions. */
287 if (uVersion != FLASH_SAVED_STATE_VERSION)
288 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
289
290 return flashR3LoadExec(&pThis->Core, pDevIns, pSSM);
291}
292#endif
293
294/**
295 * @interface_method_impl{PDMDEVREG,pfnReset}
296 */
297static DECLCALLBACK(void) flashR3Reset(PPDMDEVINS pDevIns)
298{
299 PDEVFLASHCFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVFLASHCFI);
300
301 /*
302 * Initialize the device state.
303 */
304 pThis->u16Cmd = FLASH_CFI_CMD_ARRAY_READ;
305 pThis->bStatus = FLASH_CFI_SR_PROGRAM_ERASE; /* Prgram/Erase controller is inactive. */
306 pThis->cBusCycle = 0;
307}
308
309
310/**
311 * @interface_method_impl{PDMDEVREG,pfnDestruct}
312 */
313static DECLCALLBACK(int) flashR3Destruct(PPDMDEVINS pDevIns)
314{
315 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
316 PDEVFLASHCFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVFLASHCFI);
317
318 if (pThis->pszFlashFile)
319 {
320 RTFILE hFlashFile = NIL_RTFILE;
321
322 int rc = RTFileOpen(&hFlashFile, pThis->pszFlashFile, RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
323 if (RT_FAILURE(rc))
324 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to open flash file"));
325
326 rc = RTFileWrite(hFlashFile, pThis->pbFlash, pThis->cbFlashSize, NULL);
327 RTFileClose(hFlashFile);
328 if (RT_FAILURE(rc))
329 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to write flash file"));
330
331 PDMDevHlpMMHeapFree(pDevIns, pThis->pszFlashFile);
332 pThis->pszFlashFile = NULL;
333 }
334
335 if (pThis->pbFlash)
336 {
337 PDMDevHlpMMHeapFree(pDevIns, pThis->pbFlash);
338 pThis->pbFlash = NULL;
339 }
340
341 return VINF_SUCCESS;
342}
343
344
345/**
346 * @interface_method_impl{PDMDEVREG,pfnConstruct}
347 */
348static DECLCALLBACK(int) flashR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
349{
350 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
351 PDEVFLASHCFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVFLASHCFI);
352 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
353
354 Assert(iInstance == 0); RT_NOREF1(iInstance);
355
356 /*
357 * Validate configuration.
358 */
359 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DeviceId|BaseAddress|Size|BlockSize|FlashFile", "");
360
361 /*
362 * Read configuration.
363 */
364
365 uint16_t u16FlashId = 0;
366 int rc = pHlp->pfnCFGMQueryU16Def(pCfg, "DeviceId", &u16FlashId, 0xA289);
367 if (RT_FAILURE(rc))
368 return PDMDEV_SET_ERROR(pDevIns, rc,
369 N_("Configuration error: Querying \"DeviceId\" as an integer failed"));
370
371 /* The default base address is 2MB below 4GB. */
372 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "BaseAddress", &pThis->GCPhysFlashBase, 0xFFE00000);
373 if (RT_FAILURE(rc))
374 return PDMDEV_SET_ERROR(pDevIns, rc,
375 N_("Configuration error: Querying \"BaseAddress\" as an integer failed"));
376
377 /* The default flash device size is 128K. */
378 uint32_t cbFlash = 0;
379 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Size", &cbFlash, 128 * _1K);
380 if (RT_FAILURE(rc))
381 return PDMDEV_SET_ERROR(pDevIns, rc,
382 N_("Configuration error: Querying \"Size\" as an integer failed"));
383
384 /* The default flash device block size is 4K. */
385 uint16_t cbBlock = 0;
386 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "BlockSize", &cbBlock, _4K);
387 if (RT_FAILURE(rc))
388 return PDMDEV_SET_ERROR(pDevIns, rc,
389 N_("Configuration error: Querying \"BlockSize\" as an integer failed"));
390
391 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "FlashFile", &pThis->pszFlashFile);
392 if (RT_FAILURE(rc))
393 return PDMDEV_SET_ERROR(pDevIns, rc,
394 N_("Configuration error: Querying \"FlashFile\" as a string failed"));
395
396 /*
397 * Initialize the flash core.
398 */
399 pThis->u16FlashId = u16FlashId;
400 pThis->cbBlockSize = cbBlock;
401 pThis->cbFlashSize = cbFlash;
402
403 /* Set up the flash data. */
404 pThis->pbFlash = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbFlashSize);
405 if (!pThis->pbFlash)
406 return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY, N_("Failed to allocate heap memory"));
407
408 /* Default value for empty flash. */
409 memset(pThis->pbFlash, 0xff, pThis->cbFlashSize);
410
411 /* Reset the dynamic state.*/
412 flashR3Reset(pDevIns);
413
414 RTFILE hFlashFile = NIL_RTFILE;
415 rc = RTFileOpen(&hFlashFile, pThis->pszFlashFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
416 if (RT_FAILURE(rc))
417 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to open flash file"));
418
419 size_t cbRead = 0;
420 rc = RTFileRead(hFlashFile, pThis->pbFlash, pThis->cbFlashSize, &cbRead);
421 RTFileClose(hFlashFile);
422 if (RT_FAILURE(rc))
423 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to read flash file"));
424 LogRel(("flash-cfi#%u: Read %zu bytes from file (asked for %u)\n.", cbRead, pThis->cbFlashSize, iInstance));
425
426
427 /*
428 * Register MMIO region.
429 */
430 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, pThis->GCPhysFlashBase, cbFlash,
431 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, NULL, UINT32_MAX,
432 flashMMIOWrite, flashMMIORead, NULL, NULL, "Flash Memory", &pThis->hMmio);
433 AssertRCReturn(rc, rc);
434 LogRel(("Registered %uKB flash at %RGp\n", pThis->cbFlashSize / _1K, pThis->GCPhysFlashBase));
435
436#if 0 /** @todo Later */
437 /*
438 * Register saved state.
439 */
440 rc = PDMDevHlpSSMRegister(pDevIns, FLASH_SAVED_STATE_VERSION, sizeof(*pThis), flashSaveExec, flashLoadExec);
441 AssertRCReturn(rc, rc);
442#endif
443
444 return VINF_SUCCESS;
445}
446
447
448/**
449 * The device registration structure.
450 */
451const PDMDEVREG g_DeviceFlashCFI =
452{
453 /* .u32Version = */ PDM_DEVREG_VERSION,
454 /* .uReserved0 = */ 0,
455 /* .szName = */ "flash-cfi",
456 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
457 /* .fClass = */ PDM_DEVREG_CLASS_ARCH,
458 /* .cMaxInstances = */ 1,
459 /* .uSharedVersion = */ 42,
460 /* .cbInstanceShared = */ sizeof(DEVFLASHCFI),
461 /* .cbInstanceCC = */ 0,
462 /* .cbInstanceRC = */ 0,
463 /* .cMaxPciDevices = */ 0,
464 /* .cMaxMsixVectors = */ 0,
465 /* .pszDescription = */ "Flash Memory Device",
466#if defined(IN_RING3)
467 /* .pszRCMod = */ "",
468 /* .pszR0Mod = */ "",
469 /* .pfnConstruct = */ flashR3Construct,
470 /* .pfnDestruct = */ flashR3Destruct,
471 /* .pfnRelocate = */ NULL,
472 /* .pfnMemSetup = */ NULL,
473 /* .pfnPowerOn = */ NULL,
474 /* .pfnReset = */ flashR3Reset,
475 /* .pfnSuspend = */ NULL,
476 /* .pfnResume = */ NULL,
477 /* .pfnAttach = */ NULL,
478 /* .pfnDetach = */ NULL,
479 /* .pfnQueryInterface = */ NULL,
480 /* .pfnInitComplete = */ NULL,
481 /* .pfnPowerOff = */ NULL,
482 /* .pfnSoftReset = */ NULL,
483 /* .pfnReserved0 = */ NULL,
484 /* .pfnReserved1 = */ NULL,
485 /* .pfnReserved2 = */ NULL,
486 /* .pfnReserved3 = */ NULL,
487 /* .pfnReserved4 = */ NULL,
488 /* .pfnReserved5 = */ NULL,
489 /* .pfnReserved6 = */ NULL,
490 /* .pfnReserved7 = */ NULL,
491#elif defined(IN_RING0)
492 /* .pfnEarlyConstruct = */ NULL,
493 /* .pfnConstruct = */ NULL,
494 /* .pfnDestruct = */ NULL,
495 /* .pfnFinalDestruct = */ NULL,
496 /* .pfnRequest = */ NULL,
497 /* .pfnReserved0 = */ NULL,
498 /* .pfnReserved1 = */ NULL,
499 /* .pfnReserved2 = */ NULL,
500 /* .pfnReserved3 = */ NULL,
501 /* .pfnReserved4 = */ NULL,
502 /* .pfnReserved5 = */ NULL,
503 /* .pfnReserved6 = */ NULL,
504 /* .pfnReserved7 = */ NULL,
505#elif defined(IN_RC)
506 /* .pfnConstruct = */ NULL,
507 /* .pfnReserved0 = */ NULL,
508 /* .pfnReserved1 = */ NULL,
509 /* .pfnReserved2 = */ NULL,
510 /* .pfnReserved3 = */ NULL,
511 /* .pfnReserved4 = */ NULL,
512 /* .pfnReserved5 = */ NULL,
513 /* .pfnReserved6 = */ NULL,
514 /* .pfnReserved7 = */ NULL,
515#else
516# error "Not in IN_RING3, IN_RING0 or IN_RC!"
517#endif
518 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
519};
520
521#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette