/* $Id: DevFlash.cpp 81250 2019-10-14 11:41:24Z vboxsync $ */ /** @file * DevFlash - A simple Flash device * * A simple non-volatile byte-wide (x8) memory device modeled after Intel 28F008 * FlashFile. See 28F008SA datasheet, Intel order number 290429-007. * * Implemented as an MMIO device attached directly to the CPU, not behind any * bus. Typically mapped as part of the firmware image. */ /* * Copyright (C) 2018-2019 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DEV_FLASH #include #include #include #include #include #include #include "VBoxDD.h" #include "FlashCore.h" /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * The flash device */ typedef struct DEVFLASH { /** The flash core device instance.*/ FLASHCORE Core; /** The guest physical memory base address. */ RTGCPHYS GCPhysFlashBase; /** When set, indicates the state was saved. */ bool fStateSaved; /** The file conaining the flash content. */ char *pszFlashFile; } DEVFLASH; /** Pointer to the Flash device state. */ typedef DEVFLASH *PDEVFLASH; #ifndef VBOX_DEVICE_STRUCT_TESTCASE #ifdef IN_RING3 /* for now */ /** @callback_method_impl{FNIOMMIWRITE, Flash memory write} */ PDMBOTHCBDECL(int) flashMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb) { PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH); RT_NOREF1(pvUser); return flashWrite(&pThis->Core, GCPhysAddr - pThis->GCPhysFlashBase, pv, cb); } /** @callback_method_impl{FNIOMMIOREAD, Flash memory read} */ PDMBOTHCBDECL(int) flashMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb) { PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH); RT_NOREF1(pvUser); return flashRead(&pThis->Core, GCPhysAddr - pThis->GCPhysFlashBase, pv, cb); } #endif /* IN_RING3 for now */ #ifdef IN_RING3 /** @callback_method_impl{FNSSMDEVSAVEEXEC} */ static DECLCALLBACK(int) flashSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH); int rc = flashR3SsmSaveExec(&pThis->Core, pSSM); if (RT_SUCCESS(rc)) pThis->fStateSaved = true; return rc; } /** @callback_method_impl{FNSSMDEVLOADEXEC} */ static DECLCALLBACK(int) flashLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) { PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH); Assert(uPass == SSM_PASS_FINAL); NOREF(uPass); /* Fend off unsupported versions. */ if (uVersion != FLASH_SAVED_STATE_VERSION) return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION; return flashR3SsmLoadExec(&pThis->Core, pSSM); } /** * @interface_method_impl{PDMDEVREG,pfnReset} */ static DECLCALLBACK(void) flashReset(PPDMDEVINS pDevIns) { PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH); flashR3Reset(&pThis->Core); } /** * @interface_method_impl{PDMDEVREG,pfnDestruct} */ static DECLCALLBACK(int) flashDestruct(PPDMDEVINS pDevIns) { PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH); int rc; if (!pThis->fStateSaved) { rc = flashR3SaveToFile(&pThis->Core, pThis->pszFlashFile); if (RT_FAILURE(rc)) LogRel(("Flash: Failed to save flash file")); } if (pThis->pszFlashFile) { PDMDevHlpMMHeapFree(pDevIns, pThis->pszFlashFile); pThis->pszFlashFile = NULL; } flashR3Destruct(&pThis->Core); return VINF_SUCCESS; } /** * @interface_method_impl{PDMDEVREG,pfnConstruct} */ static DECLCALLBACK(int) flashConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) { RT_NOREF1(iInstance); PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH); Assert(iInstance == 0); /* * Validate configuration. */ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DeviceId|BaseAddress|Size|BlockSize|FlashFile", ""); /* * Read configuration. */ /* The default device ID is Intel 28F800SA. */ uint16_t u16FlashId = 0; int rc = CFGMR3QueryU16Def(pCfg, "DeviceId", &u16FlashId, 0xA289); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"DeviceId\" as an integer failed")); /* The default base address is 2MB below 4GB. */ rc = CFGMR3QueryU64Def(pCfg, "BaseAddress", &pThis->GCPhysFlashBase, 0xFFE00000); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"BaseAddress\" as an integer failed")); /* The default flash device size is 128K. */ uint32_t cbFlash = 0; rc = CFGMR3QueryU32Def(pCfg, "Size", &cbFlash, 128 * _1K); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"Size\" as an integer failed")); /* The default flash device block size is 4K. */ uint16_t cbBlock = 0; rc = CFGMR3QueryU16Def(pCfg, "BlockSize", &cbBlock, _4K); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"BlockSize\" as an integer failed")); rc = CFGMR3QueryStringAlloc(pCfg, "FlashFile", &pThis->pszFlashFile); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FlashFile\" as a string failed")); rc = flashR3Init(&pThis->Core, pDevIns, u16FlashId, cbFlash, cbBlock); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Flash: Failed to initialize core flash device")); /* Try to load the flash content from file. */ rc = flashR3LoadFromFile(&pThis->Core, pThis->pszFlashFile); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Flash: Failed to load flash content from given file")); /* * Register MMIO region. */ rc = PDMDevHlpMMIORegister(pDevIns, pThis->GCPhysFlashBase, cbFlash, NULL /*pvUser*/, IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU, flashMMIOWrite, flashMMIORead, "Flash Memory"); AssertRCReturn(rc, rc); LogRel(("Registered %uKB flash at %RGp\n", pThis->Core.cbFlashSize / _1K, pThis->GCPhysFlashBase)); /* * Register saved state. */ rc = PDMDevHlpSSMRegister(pDevIns, FLASH_SAVED_STATE_VERSION, sizeof(*pThis), flashSaveExec, flashLoadExec); if (RT_FAILURE(rc)) return rc; return VINF_SUCCESS; } #endif /* IN_RING3 */ /** * The device registration structure. */ const PDMDEVREG g_DeviceFlash = { /* .u32Version = */ PDM_DEVREG_VERSION, /* .uReserved0 = */ 0, /* .szName = */ "flash", /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS, /* .fClass = */ PDM_DEVREG_CLASS_ARCH, /* .cMaxInstances = */ 1, /* .uSharedVersion = */ 42, /* .cbInstanceShared = */ sizeof(DEVFLASH), /* .cbInstanceCC = */ 0, /* .cbInstanceRC = */ 0, /* .cMaxPciDevices = */ 0, /* .cMaxMsixVectors = */ 0, /* .pszDescription = */ "Flash Memory Device", #if defined(IN_RING3) /* .pszRCMod = */ "", /* .pszR0Mod = */ "", /* .pfnConstruct = */ flashConstruct, /* .pfnDestruct = */ flashDestruct, /* .pfnRelocate = */ NULL, /* .pfnMemSetup = */ NULL, /* .pfnPowerOn = */ NULL, /* .pfnReset = */ flashReset, /* .pfnSuspend = */ NULL, /* .pfnResume = */ NULL, /* .pfnAttach = */ NULL, /* .pfnDetach = */ NULL, /* .pfnQueryInterface = */ NULL, /* .pfnInitComplete = */ NULL, /* .pfnPowerOff = */ NULL, /* .pfnSoftReset = */ NULL, /* .pfnReserved0 = */ NULL, /* .pfnReserved1 = */ NULL, /* .pfnReserved2 = */ NULL, /* .pfnReserved3 = */ NULL, /* .pfnReserved4 = */ NULL, /* .pfnReserved5 = */ NULL, /* .pfnReserved6 = */ NULL, /* .pfnReserved7 = */ NULL, #elif defined(IN_RING0) /* .pfnEarlyConstruct = */ NULL, /* .pfnConstruct = */ NULL, /* .pfnDestruct = */ NULL, /* .pfnFinalDestruct = */ NULL, /* .pfnRequest = */ NULL, /* .pfnReserved0 = */ NULL, /* .pfnReserved1 = */ NULL, /* .pfnReserved2 = */ NULL, /* .pfnReserved3 = */ NULL, /* .pfnReserved4 = */ NULL, /* .pfnReserved5 = */ NULL, /* .pfnReserved6 = */ NULL, /* .pfnReserved7 = */ NULL, #elif defined(IN_RC) /* .pfnConstruct = */ NULL, /* .pfnReserved0 = */ NULL, /* .pfnReserved1 = */ NULL, /* .pfnReserved2 = */ NULL, /* .pfnReserved3 = */ NULL, /* .pfnReserved4 = */ NULL, /* .pfnReserved5 = */ NULL, /* .pfnReserved6 = */ NULL, /* .pfnReserved7 = */ NULL, #else # error "Not in IN_RING3, IN_RING0 or IN_RC!" #endif /* .u32VersionEnd = */ PDM_DEVREG_VERSION }; #endif /* VBOX_DEVICE_STRUCT_TESTCASE */