From 7421d61c066db8231575bcdacdbe9a4dfef1a66a Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 6 Sep 2025 12:54:54 -0400 Subject: [PATCH] refactor: separate out usb block erase --- src/machine/usb/msc/disk.go | 8 ++++---- src/machine/usb/msc/msc.go | 15 ++++++++------- src/machine/usb/msc/scsi_unmap.go | 22 +++++++++++++++++----- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/machine/usb/msc/disk.go b/src/machine/usb/msc/disk.go index 6624d38c01..5efb35a6d6 100644 --- a/src/machine/usb/msc/disk.go +++ b/src/machine/usb/msc/disk.go @@ -27,7 +27,6 @@ func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) { // them we assume the provided block device is aligned to the end of the underlying hardware block // device and offset all reads/writes by the remaining bytes that don't make up a full block. m.blockOffset = uint32(m.dev.Size()) % m.blockSizeUSB - // FIXME: Figure out what to do if the emulated write block size is larger than the erase block size // Set VPD UNMAP fields for i := range vpdPages { @@ -46,10 +45,11 @@ func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) { // https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf // We assume the block device is aligned to the end of the underlying block device - blockOffset := uint32(dev.EraseBlockSize()) % m.blockSizeUSB + eraseBlockOffset := uint32(dev.EraseBlockSize()) % m.blockSizeUSB + m.eraseBlockOffset = eraseBlockOffset // Set the UGAVALID bit to indicate that the UNMAP GRANULARITY ALIGNMENT is valid - blockOffset |= 0x80000000 - binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], blockOffset) + eraseBlockOffset |= 0x80000000 + binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], eraseBlockOffset) } break } diff --git a/src/machine/usb/msc/msc.go b/src/machine/usb/msc/msc.go index d3bf8d6e29..f87bb5411e 100644 --- a/src/machine/usb/msc/msc.go +++ b/src/machine/usb/msc/msc.go @@ -42,13 +42,14 @@ type msc struct { cswBuf []byte // CSW response buffer state mscState - maxLUN uint8 // Maximum Logical Unit Number (n-1 for n LUNs) - dev machine.BlockDevice - blockCount uint32 // Number of blocks in the device - blockOffset uint32 // Byte offset of the first block in the device for aligned writes - blockSizeUSB uint32 // Write block size as presented to the host over USB - blockSizeRaw uint32 // Write block size of the underlying device hardware - readOnly bool + maxLUN uint8 // Maximum Logical Unit Number (n-1 for n LUNs) + dev machine.BlockDevice + blockCount uint32 // Number of blocks in the device + blockOffset uint32 // Byte offset of the first block in the device for aligned writes + blockSizeUSB uint32 // Write block size as presented to the host over USB + blockSizeRaw uint32 // Write block size of the underlying device hardware + eraseBlockOffset uint32 // Number of USB blocks before the first full erase block in the device + readOnly bool vendorID [8]byte // Max 8 ASCII characters productID [16]byte // Max 16 ASCII characters diff --git a/src/machine/usb/msc/scsi_unmap.go b/src/machine/usb/msc/scsi_unmap.go index 79c2426ddc..8a04ad999f 100644 --- a/src/machine/usb/msc/scsi_unmap.go +++ b/src/machine/usb/msc/scsi_unmap.go @@ -9,13 +9,16 @@ import ( type Error int const ( - errorLBAOutOfRange Error = iota + errorLBAOutOfRange Error = iota + errorEraseBlockMisaligned Error ) func (e Error) Error() string { switch e { case errorLBAOutOfRange: return "LBA out of range" + case errorEraseBlockMisaligned: + return "Erase attempted on partial erase block" default: return "unknown error" } @@ -49,7 +52,7 @@ func (m *msc) scsiUnmap(b []byte) { // Unmap the blocks we can from this packet for i := 8; i < descEnd; i += 16 { - err := m.unmapBlocksFromDescriptor(b[i:], uint64(m.blockCount)) + err := m.unmapBlocksFromDescriptor(b[i:]) if err != nil { // TODO: Might need a better error code here for device errors? m.sendScsiError(csw.StatusFailed, scsi.SenseVolumeOverflow, scsi.SenseCodeLBAOutOfRange) @@ -67,7 +70,7 @@ func (m *msc) scsiUnmap(b []byte) { } } -func (m *msc) unmapBlocksFromDescriptor(b []byte, numBlocks uint64) error { +func (m *msc) unmapBlocksFromDescriptor(b []byte) error { blockCount := binary.BigEndian.Uint32(b[8:12]) if blockCount == 0 { // No blocks to unmap. Explicitly not an error per the spec @@ -82,9 +85,18 @@ func (m *msc) unmapBlocksFromDescriptor(b []byte, numBlocks uint64) error { return errorLBAOutOfRange } + return m.unmapUSBBlocks(lba, blockCount) +} + +func (m *msc) unmapUSBBlocks(firstBlock, numBlocks uint32) error { + if firstBlock%m.blockSizeUSB != m.eraseBlockOffset { + // The starting block is not aligned to the underlying hardware erase block + return errorEraseBlockMisaligned + } + // Convert the emulated block size to the underlying hardware erase block size - blockStart := int64(lba*m.blockSizeUSB) / m.dev.EraseBlockSize() - rawBlockCount := int64(blockCount*m.blockSizeUSB) / m.dev.EraseBlockSize() + blockStart := int64(firstBlock*m.blockSizeUSB) / m.dev.EraseBlockSize() + rawBlockCount := int64(numBlocks*m.blockSizeUSB) / m.dev.EraseBlockSize() // Unmap the blocks return m.dev.EraseBlocks(blockStart, rawBlockCount)