/** Implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks() function. @param This The EFI_BLOCK_IO_PROTOCOL instance. @param MediaId The media id that the write request is for. @param LBA The starting logical block address to read from on the device. The caller is responsible for writing to only legitimate locations. @param BufferSize The size of the Buffer in bytes. This must be a multiple of the intrinsic block size of the device. @param Buffer A pointer to the destination buffer for the data. The caller is responsible for either having implicit or explicit ownership of the buffer. @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR Hardware Error @retval EFI_INVALID_PARAMETER Parameter is error @retval EFI_NO_MEDIA No media @retval EFI_MEDIA_CHANGED Media Change @retval EFI_BAD_BUFFER_SIZE Buffer size is bad **/ EFI_STATUS EFIAPI MMCSDBlockReadBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This, IN UINT32 MediaId, IN EFI_LBA LBA, IN UINTN BufferSize, OUT VOID *Buffer ) { EFI_STATUS Status; UINT32 Address; CARD_DATA *CardData; EFI_SD_HOST_IO_PROTOCOL *SDHostIo; UINT32 RemainingLength; UINT32 TransferLength; UINT8 *BufferPointer; BOOLEAN SectorAddressing; UINTN TotalBlock; DEBUG((EFI_D_INFO, "Read(LBA=%08lx, Buffer=%08x, Size=%08x)\n", LBA, Buffer, BufferSize)); Status = EFI_SUCCESS; CardData = CARD_DATA_FROM_THIS(This); SDHostIo = CardData->SDHostIo; if (MediaId != CardData->BlockIoMedia.MediaId) { return EFI_MEDIA_CHANGED; } if (ModU64x32 (BufferSize,CardData->BlockIoMedia.BlockSize) != 0) { return EFI_BAD_BUFFER_SIZE; } if ((CardData->CardType == SDMemoryCard2High) || (CardData->CardType == MMCCardHighCap)) { SectorAddressing = TRUE; } else { SectorAddressing = FALSE; } if (SectorAddressing) { // //Block Address // Address = (UINT32)DivU64x32 (MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize), 512); } else { // //Byte Address // Address = (UINT32)MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize); } TotalBlock = (UINTN) DivU64x32 (BufferSize, CardData->BlockIoMedia.BlockSize); if (LBA + TotalBlock > CardData->BlockIoMedia.LastBlock + 1) { return EFI_INVALID_PARAMETER; } if (!Buffer) { Status = EFI_INVALID_PARAMETER; DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks:Invalid parameter \r\n")); goto Done; } if ((BufferSize % CardData->BlockIoMedia.BlockSize) != 0) { Status = EFI_BAD_BUFFER_SIZE; DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: Bad buffer size \r\n")); goto Done; } if (BufferSize == 0) { Status = EFI_SUCCESS; goto Done; } BufferPointer = Buffer; RemainingLength = (UINT32)BufferSize; while (RemainingLength > 0) { if ((BufferSize > CardData->BlockIoMedia.BlockSize)) { if (RemainingLength > SDHostIo->HostCapability.BoundarySize) { TransferLength = SDHostIo->HostCapability.BoundarySize; } else { TransferLength = RemainingLength; } if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) { if (!(CardData->ExtCSDRegister.CARD_TYPE & (BIT2 | BIT3))) { Status = SendCommand ( CardData, SET_BLOCKLEN, CardData->BlockIoMedia.BlockSize, NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { break; } } Status = SendCommand ( CardData, SET_BLOCK_COUNT, TransferLength / CardData->BlockIoMedia.BlockSize, NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { break; } } Status = SendCommand ( CardData, READ_MULTIPLE_BLOCK, Address, InData, CardData->AlignedBuffer, TransferLength, ResponseR1, TIMEOUT_DATA, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: READ_MULTIPLE_BLOCK -> Fail\n")); break; } } else { if (RemainingLength > CardData->BlockIoMedia.BlockSize) { TransferLength = CardData->BlockIoMedia.BlockSize; } else { TransferLength = RemainingLength; } Status = SendCommand ( CardData, READ_SINGLE_BLOCK, Address, InData, CardData->AlignedBuffer, (UINT32)TransferLength, ResponseR1, TIMEOUT_DATA, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: READ_SINGLE_BLOCK -> Fail\n")); break; } } CopyMem (BufferPointer, CardData->AlignedBuffer, TransferLength); if (SectorAddressing) { // //Block Address // Address += TransferLength / 512; } else { // //Byte Address // Address += TransferLength; } BufferPointer += TransferLength; RemainingLength -= TransferLength; } if (EFI_ERROR (Status)) { if ((CardData->CardType == SDMemoryCard) || (CardData->CardType == SDMemoryCard2)|| (CardData->CardType == SDMemoryCard2High)) { SendCommand ( CardData, STOP_TRANSMISSION, 0, NoData, NULL, 0, ResponseR1b, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); } else { SendCommand ( CardData, STOP_TRANSMISSION, 0, NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); } } Done: DEBUG((EFI_D_INFO, "MMCSDBlockReadBlocks: Status = %r\n", Status)); return Status; }
STATIC EFI_STATUS EFIAPI VirtioBlkInit ( IN OUT VBLK_DEV *Dev ) { UINT8 NextDevStat; EFI_STATUS Status; UINT64 Features; UINT64 NumSectors; UINT32 BlockSize; UINT8 PhysicalBlockExp; UINT8 AlignmentOffset; UINT32 OptIoSize; UINT16 QueueSize; UINT64 RingBaseShift; PhysicalBlockExp = 0; AlignmentOffset = 0; OptIoSize = 0; // // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. // NextDevStat = 0; // step 1 -- reset device Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); if (EFI_ERROR (Status)) { goto Failed; } NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); if (EFI_ERROR (Status)) { goto Failed; } NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); if (EFI_ERROR (Status)) { goto Failed; } // // Set Page Size - MMIO VirtIo Specific // Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE); if (EFI_ERROR (Status)) { goto Failed; } // // step 4a -- retrieve and validate features // Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features); if (EFI_ERROR (Status)) { goto Failed; } Status = VIRTIO_CFG_READ (Dev, Capacity, &NumSectors); if (EFI_ERROR (Status)) { goto Failed; } if (NumSectors == 0) { Status = EFI_UNSUPPORTED; goto Failed; } if (Features & VIRTIO_BLK_F_BLK_SIZE) { Status = VIRTIO_CFG_READ (Dev, BlkSize, &BlockSize); if (EFI_ERROR (Status)) { goto Failed; } if (BlockSize == 0 || BlockSize % 512 != 0 || ModU64x32 (NumSectors, BlockSize / 512) != 0) { // // We can only handle a logical block consisting of whole sectors, // and only a disk composed of whole logical blocks. // Status = EFI_UNSUPPORTED; goto Failed; } } else { BlockSize = 512; } if (Features & VIRTIO_BLK_F_TOPOLOGY) { Status = VIRTIO_CFG_READ (Dev, Topology.PhysicalBlockExp, &PhysicalBlockExp); if (EFI_ERROR (Status)) { goto Failed; } if (PhysicalBlockExp >= 32) { Status = EFI_UNSUPPORTED; goto Failed; } Status = VIRTIO_CFG_READ (Dev, Topology.AlignmentOffset, &AlignmentOffset); if (EFI_ERROR (Status)) { goto Failed; } Status = VIRTIO_CFG_READ (Dev, Topology.OptIoSize, &OptIoSize); if (EFI_ERROR (Status)) { goto Failed; } } Features &= VIRTIO_BLK_F_BLK_SIZE | VIRTIO_BLK_F_TOPOLOGY | VIRTIO_BLK_F_RO | VIRTIO_BLK_F_FLUSH | VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM; // // In virtio-1.0, feature negotiation is expected to complete before queue // discovery, and the device can also reject the selected set of features. // if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) { Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat); if (EFI_ERROR (Status)) { goto Failed; } } // // step 4b -- allocate virtqueue // Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, 0); if (EFI_ERROR (Status)) { goto Failed; } Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize); if (EFI_ERROR (Status)) { goto Failed; } if (QueueSize < 3) { // SynchronousRequest() uses at most three descriptors Status = EFI_UNSUPPORTED; goto Failed; } Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring); if (EFI_ERROR (Status)) { goto Failed; } // // If anything fails from here on, we must release the ring resources // Status = VirtioRingMap ( Dev->VirtIo, &Dev->Ring, &RingBaseShift, &Dev->RingMap ); if (EFI_ERROR (Status)) { goto ReleaseQueue; } // // Additional steps for MMIO: align the queue appropriately, and set the // size. If anything fails from here on, we must unmap the ring resources. // Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize); if (EFI_ERROR (Status)) { goto UnmapQueue; } Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE); if (EFI_ERROR (Status)) { goto UnmapQueue; } // // step 4c -- Report GPFN (guest-physical frame number) of queue. // Status = Dev->VirtIo->SetQueueAddress ( Dev->VirtIo, &Dev->Ring, RingBaseShift ); if (EFI_ERROR (Status)) { goto UnmapQueue; } // // step 5 -- Report understood features. // if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM); Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features); if (EFI_ERROR (Status)) { goto UnmapQueue; } } // // step 6 -- initialization complete // NextDevStat |= VSTAT_DRIVER_OK; Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); if (EFI_ERROR (Status)) { goto UnmapQueue; } // // Populate the exported interface's attributes; see UEFI spec v2.4, 12.9 EFI // Block I/O Protocol. // Dev->BlockIo.Revision = 0; Dev->BlockIo.Media = &Dev->BlockIoMedia; Dev->BlockIo.Reset = &VirtioBlkReset; Dev->BlockIo.ReadBlocks = &VirtioBlkReadBlocks; Dev->BlockIo.WriteBlocks = &VirtioBlkWriteBlocks; Dev->BlockIo.FlushBlocks = &VirtioBlkFlushBlocks; Dev->BlockIoMedia.MediaId = 0; Dev->BlockIoMedia.RemovableMedia = FALSE; Dev->BlockIoMedia.MediaPresent = TRUE; Dev->BlockIoMedia.LogicalPartition = FALSE; Dev->BlockIoMedia.ReadOnly = (BOOLEAN) ((Features & VIRTIO_BLK_F_RO) != 0); Dev->BlockIoMedia.WriteCaching = (BOOLEAN) ((Features & VIRTIO_BLK_F_FLUSH) != 0); Dev->BlockIoMedia.BlockSize = BlockSize; Dev->BlockIoMedia.IoAlign = 0; Dev->BlockIoMedia.LastBlock = DivU64x32 (NumSectors, BlockSize / 512) - 1; DEBUG ((DEBUG_INFO, "%a: LbaSize=0x%x[B] NumBlocks=0x%Lx[Lba]\n", __FUNCTION__, Dev->BlockIoMedia.BlockSize, Dev->BlockIoMedia.LastBlock + 1)); if (Features & VIRTIO_BLK_F_TOPOLOGY) { Dev->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; Dev->BlockIoMedia.LowestAlignedLba = AlignmentOffset; Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock = 1u << PhysicalBlockExp; Dev->BlockIoMedia.OptimalTransferLengthGranularity = OptIoSize; DEBUG ((DEBUG_INFO, "%a: FirstAligned=0x%Lx[Lba] PhysBlkSize=0x%x[Lba]\n", __FUNCTION__, Dev->BlockIoMedia.LowestAlignedLba, Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock)); DEBUG ((DEBUG_INFO, "%a: OptimalTransferLengthGranularity=0x%x[Lba]\n", __FUNCTION__, Dev->BlockIoMedia.OptimalTransferLengthGranularity)); } return EFI_SUCCESS; UnmapQueue: Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap); ReleaseQueue: VirtioRingUninit (Dev->VirtIo, &Dev->Ring); Failed: // // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device // Status. VirtIo access failure here should not mask the original error. // NextDevStat |= VSTAT_FAILED; Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); return Status; // reached only via Failed above }
/** Reads the requested number of blocks from the device. This function implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks(). It reads the requested number of blocks from the device. All the blocks are read, or an error is returned. @param This Indicates a pointer to the calling context. @param ReadData If TRUE then read data. If FALSE then write data. @param MediaId The media ID that the read request is for. @param Lba The starting logical block address to read from on the device. @param BufferSize The size of the Buffer in bytes. This must be a multiple of the intrinsic block size of the device. @param Buffer A pointer to the destination buffer for the data. The caller is responsible for either having implicit or explicit ownership of the buffer. @retval EFI_SUCCESS The data was read correctly from the device. @retval EFI_DEVICE_ERROR The device reported an error while attempting to perform the read operation. @retval EFI_NO_MEDIA There is no media in the device. @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the intrinsic block size of the device. @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, or the buffer is not on proper alignment. **/ EFI_STATUS EFIAPI ReadOrWriteBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This, IN BOOLEAN ReadData, IN UINT32 MediaId, IN EFI_LBA Lba, IN UINTN BufferSize, OUT VOID *Buffer ) { EFI_STATUS Status; BLOCK_MMIO_TO_BLOCK_IO_DEVICE *Private; UINTN TotalBlock; EFI_BLOCK_IO_MEDIA *Media; UINT64 Address; UINTN Count; EFI_CPU_IO_PROTOCOL_IO_MEM CpuAccessFunction; // // First, validate the parameters // if ((Buffer == NULL) || (BufferSize == 0)) { return EFI_INVALID_PARAMETER; } // // Get private data structure // Private = PRIVATE_FROM_BLOCK_IO (This); Media = Private->BlockMmio->Media; // // BufferSize must be a multiple of the intrinsic block size of the device. // if (ModU64x32 (BufferSize, Media->BlockSize) != 0) { return EFI_BAD_BUFFER_SIZE; } TotalBlock = (UINTN) DivU64x32 (BufferSize, Media->BlockSize); // // Make sure the range to read is valid. // if (Lba + TotalBlock - 1 > Media->LastBlock) { return EFI_INVALID_PARAMETER; } if (!(Media->MediaPresent)) { return EFI_NO_MEDIA; } if (MediaId != Media->MediaId) { return EFI_MEDIA_CHANGED; } Address = Private->BlockMmio->BaseAddress; Address += MultU64x32 (Lba, Media->BlockSize); Count = BufferSize >> 3; if (ReadData) { CpuAccessFunction = Private->CpuIo->Mem.Read; } else { CpuAccessFunction = Private->CpuIo->Mem.Write; } Status = (CpuAccessFunction) ( Private->CpuIo, EfiCpuIoWidthUint64, Address, Count, Buffer ); return Status; }