Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix endianness issues on USB mass storage CBW and CSW signatures.
USB mass storage works by wrapping other storage protocols in USB packets. There are several defined, but universally the most common one is SCSI. While SCSI is big-endian, USB is little-endian. The wrappers used in the USB mass storage protocol have some "signature" (magic number) fields that are part of the USB protocol, and hence little-endian. Most OSs apparently don't check these signatures, but some (such as FreeBSD) do. The previous code encodes these signatures as big-endian. This patch fixes them to be little-endian. (Technical background follows for the curious. This is oriented to SCSI under USB Bulk-Only mass storage protocol, which is by far the predominant protocol used by USB mass storage devices, including the UF2 bootloader here.) A CBW (Command Block Wrapper) is a SCSI command (or other protocol, but typically SCSI) embedded in a USB mass storage packet. A CSW (Command Status Wrapper) is a SCSI status (reply) embedded in a USB mass storage packet. The CBW/CSW mechanism embeds any SCSI protocol transaction. The first command is typically an INQUIRY command (which retrieves metadata about the drive), but for simplicity, we'll talk about a READ command. A typical "read" transaction is as folllows: 1. The host sends the device a SCSI read command (SCSI CDB block), encapsulated in a USB mass storage CBW packet (which is further encapsulated in USB bulk data packets, addressed to the device's read endpoint). 2. If the device is able to fulfill the request, it sends the host the requested data, with no encapsulation at the USB mass storage level. (These packets are still encapsulated at the protocol layer, addressed from the device's write endpoint). 3. If the device is unable to fulfill the request, it sends a USB STALL packet (which is at the protocol level, lower than the mass storage level). 4. Regardless of whether the device was able to fulfill the request or not, it sends a SCSI status packet, encapsulated in a USB mass storage CSW packet (which is further encapsulated in USB bulk data packets, addressed from the device's write endpoint). If everything works well, the host now can unambiguously tell whether the command succeeded or failed, based on the presence / absence of the STALL packet, and interprets the next packet as a SCSI status packet (embedded in a CSW wrapper). If everything isn't working well, the host and device can get out of sync in these state transitions. To ensure that everything is working properly, every SCSI command or status is wrapped in a packet specific to the USB mass storage protocol: a CBW for a command, or CSW for a status (reply). The CBW starts with the magic number 0x43425355 (little-endian, "USBC"), followed by a tag used to identify this command. The CSW starts with the magic number 0x53425355 (little-endian, "USBS"), followed by the tag of the command being responded to. (The endianness of the tag is irrelevant, since it's only a four-byte sequence that needs to be the same in the CSW as in the initiating CBW.) Since the CBW's and CSW's magic numbers are typically irrelevant, they can usually be ignored. Most OSs' implementations of the USB mass storage protocol ignore them. However, they serve as a good check to ensure that the host and device are in sync; hence, some OSs (such as FreeBSD) do validate them. Since the CBW and CSW are defined as part of the USB spec, their signatures are little-endian, even though the values in the wrapped packets are big-endian. As described above, these signatures are ignored by many OSs, but are validated by FreeBSD. I'm surprised that try_read_cbw ever worked if USE_MSC_CHECKS was enabled. I patched the code within USE_MSC_CHECKS, but haven't actually tested it: I only tested the default build.
- Loading branch information