
I was trying to read files from a Samsung MZ-VLB1T0B NVME device, that was formatted with an ext4 filesystem. But I observed some memory corruptions for the loaded file in u-boot. Closer investigation revealed that the u-boot nvme driver can't handle large IO-operations that are not aligned with the 4KB pages of the NVME device. In detail the nvme driver would split the read operation into 2MB chunks. Due to the misalignment 513 prp-entries are needed for reading one such 2MB chunk. The driver split the read into 1 entry for prp1 and a prp-list containing 512 entries for prp2. The issue is now that the driver splitted this list across 2 pages (first page with 511 entries and pointer to the second page, second page containing a single entry). This split violated the nvme-spec (as all 512 entries of the list would fit on a single page) and caused the observed memory corruption. The provided patch fixes the issue. END
Alexander Sowarka (1): nvme: Fix multipage prp-list
drivers/nvme/nvme.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)

The nvme driver falsely assumed that the last entry on a page of the prp-list always points to the next page of the prp-list. This potentially can lead to the illegal creation of pages on the prp-list with only a single entry. This change now ensures that splitting the prp-list into multiple pages, behaves now as required by the NVME-Spec.
Related to this, also the size of the memory allocation is adjusted accordingly.
Signed-off-by: Alexander Sowarka alexander.sowarka@aerq.com ---
drivers/nvme/nvme.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index 1d56517e99..55cce0a778 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -72,7 +72,7 @@ static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2, }
nprps = DIV_ROUND_UP(length, page_size); - num_pages = DIV_ROUND_UP(nprps, prps_per_page); + num_pages = DIV_ROUND_UP(nprps - 1, prps_per_page - 1);
if (nprps > dev->prp_entry_num) { free(dev->prp_pool); @@ -85,13 +85,13 @@ static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2, printf("Error: malloc prp_pool fail\n"); return -ENOMEM; } - dev->prp_entry_num = prps_per_page * num_pages; + dev->prp_entry_num = num_pages * (prps_per_page - 1) + 1; }
prp_pool = dev->prp_pool; i = 0; while (nprps) { - if (i == ((page_size >> 3) - 1)) { + if ((i == (prps_per_page - 1)) && nprps > 1) { *(prp_pool + i) = cpu_to_le64((ulong)prp_pool + page_size); i = 0; @@ -104,7 +104,7 @@ static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2, *prp2 = (ulong)dev->prp_pool;
flush_dcache_range((ulong)dev->prp_pool, (ulong)dev->prp_pool + - dev->prp_entry_num * sizeof(u64)); + num_pages * page_size);
return 0; }

On Sun, Aug 28, 2022 at 09:30:20PM +0200, Alexander Sowarka wrote:
The nvme driver falsely assumed that the last entry on a page of the prp-list always points to the next page of the prp-list. This potentially can lead to the illegal creation of pages on the prp-list with only a single entry. This change now ensures that splitting the prp-list into multiple pages, behaves now as required by the NVME-Spec.
Related to this, also the size of the memory allocation is adjusted accordingly.
Signed-off-by: Alexander Sowarka alexander.sowarka@aerq.com
Applied to u-boot/next, thanks!
participants (2)
-
Alexander Sowarka
-
Tom Rini