/** Writes a specified number of bytes to a device. @param This Indicates a pointer to the calling context. @param MediaId ID of the medium to be written. @param Offset The starting byte offset on the logical block I/O device to write. @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. @param Buffer A pointer to the buffer containing the data to be written. @retval EFI_SUCCESS The data was written correctly to the device. @retval EFI_WRITE_PROTECTED The device can not be written to. @retval EFI_DEVICE_ERROR The device reported an error while performing the write. @retval EFI_NO_MEDIA There is no media in the device. @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not valid for the device. **/ EFI_STATUS EFIAPI NorFlashDiskIoWriteDisk ( IN EFI_DISK_IO_PROTOCOL *This, IN UINT32 MediaId, IN UINT64 DiskOffset, IN UINTN BufferSize, IN VOID *Buffer ) { NOR_FLASH_INSTANCE *Instance; UINT32 BlockSize; UINT32 BlockOffset; EFI_LBA Lba; UINTN RemainingBytes; UINTN WriteSize; EFI_STATUS Status; Instance = INSTANCE_FROM_DISKIO_THIS(This); if (MediaId != Instance->Media.MediaId) { return EFI_MEDIA_CHANGED; } BlockSize = Instance->Media.BlockSize; Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); RemainingBytes = BufferSize; // Write either all the remaining bytes, or the number of bytes that bring // us up to a block boundary, whichever is less. // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next // block boundary (even if it is already on one). WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset); do { if (WriteSize == BlockSize) { // Write a full block Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32)); } else { // Write a partial block Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer); } if (EFI_ERROR (Status)) { return Status; } // Now continue writing either all the remaining bytes or single blocks. RemainingBytes -= WriteSize; Buffer = (UINT8 *) Buffer + WriteSize; Lba++; BlockOffset = 0; WriteSize = MIN (RemainingBytes, BlockSize); } while (RemainingBytes); return Status; }
/** Writes the specified number of bytes from the input buffer to the block. The Write() function writes the specified number of bytes from the provided buffer to the specified block and offset. If the firmware volume is sticky write, the caller must ensure that all the bits of the specified range to write are in the EFI_FVB_ERASE_POLARITY state before calling the Write() function, or else the result will be unpredictable. This unpredictability arises because, for a sticky-write firmware volume, a write may negate a bit in the EFI_FVB_ERASE_POLARITY state but cannot flip it back again. Before calling the Write() function, it is recommended for the caller to first call the EraseBlocks() function to erase the specified block to write. A block erase cycle will transition bits from the (NOT)EFI_FVB_ERASE_POLARITY state back to the EFI_FVB_ERASE_POLARITY state. Implementations should be mindful that the firmware volume might be in the WriteDisabled state. If it is in this state, the Write() function must return the status code EFI_ACCESS_DENIED without modifying the contents of the firmware volume. The Write() function must also prevent spanning block boundaries. If a write is requested that spans a block boundary, the write must store up to the boundary but not beyond. The output parameter NumBytes must be set to correctly indicate the number of bytes actually written. The caller must be aware that a write may be partially completed. All writes, partial or otherwise, must be fully flushed to the hardware before the Write() service returns. @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. @param Lba The starting logical block index to write to. @param Offset Offset into the block at which to begin writing. @param NumBytes The pointer to a UINTN. At entry, *NumBytes contains the total size of the buffer. At exit, *NumBytes contains the total number of bytes actually written. @param Buffer The pointer to a caller-allocated buffer that contains the source for the write. @retval EFI_SUCCESS The firmware volume was written successfully. @retval EFI_BAD_BUFFER_SIZE The write was attempted across an LBA boundary. On output, NumBytes contains the total number of bytes actually written. @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state. @retval EFI_DEVICE_ERROR The block device is malfunctioning and could not be written. **/ EFI_STATUS EFIAPI FvbWrite ( IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, IN EFI_LBA Lba, IN UINTN Offset, IN OUT UINTN *NumBytes, IN UINT8 *Buffer ) { NOR_FLASH_INSTANCE *Instance; Instance = INSTANCE_FROM_FVB_THIS (This); return NorFlashWriteSingleBlock (Instance, Instance->StartLba + Lba, Offset, NumBytes, Buffer); }
EFI_STATUS NorFlashWriteBlocks ( IN NOR_FLASH_INSTANCE *Instance, IN EFI_LBA Lba, IN UINTN BufferSizeInBytes, IN VOID *Buffer ) { UINT32 *pWriteBuffer; EFI_STATUS Status = EFI_SUCCESS; EFI_LBA CurrentBlock; UINT32 BlockSizeInWords; UINT32 NumBlocks; UINT32 BlockCount; // The buffer must be valid if (Buffer == NULL) { return EFI_INVALID_PARAMETER; } if(Instance->Media.ReadOnly == TRUE) { return EFI_WRITE_PROTECTED; } // We must have some bytes to read DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes)); if(BufferSizeInBytes == 0) { return EFI_BAD_BUFFER_SIZE; } // The size of the buffer must be a multiple of the block size DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize)); if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { return EFI_BAD_BUFFER_SIZE; } // All blocks must be within the device NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba)); if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n")); return EFI_INVALID_PARAMETER; } BlockSizeInWords = Instance->Media.BlockSize / 4; // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer // to a proper data type, so use *ReadBuffer pWriteBuffer = (UINT32 *)Buffer; CurrentBlock = Lba; for (BlockCount=0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) { DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock)); Status = NorFlashWriteSingleBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords); if (EFI_ERROR(Status)) { break; } } DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status)); return Status; }