From 6fbe06a6ce734b08ba723cc8acde0fded744b686 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 18 Jun 2021 15:09:30 +0800 Subject: [PATCH 01/11] x86: Discard .note.gnu.property sections When switching to kernel.org x86_64 gcc 11.1.0 toolchain, u-boot.rom built from qemu-x86_defconfig no longer boots anymore. Investigation shows that U-Boot fails at a very early stage during the boot process, in fdtdec_prepare_fdt() where fdt_check_header() complains that there is not a valid device tree found at gd->fdt_blob which points to _end. Now _end points to an allocated section .note.gnu.property which of course is wrong. This issue is however not seen when using the default Ubuntu 20.04 gnu toolchain (gcc 9.3.0 with binutils 2.34). Further investigation shows that it is caused by a behavior change of binutils v2.36 which is part of the kernel.org gcc 11.1.0 toolchain, via the following commit: 939b95c77bf2 ("Linux/x86: Configure gas with --enable-x86-used-note by default") In fact, there was already a regression bug report [1] for binutils two months ago, but the binutils folks did not think it is a bug :( To resolve this, there are several options: * pass -Wa,-mx86-used-note=no to gas * pass -R .note.gnu.property to objcopy * discard the section in the linker script Linux kernel uses the discard way [2], so let's do the same for U-Boot. [1] https://sourceware.org/bugzilla/show_bug.cgi?id=27753 [2] commit 4caffe6a28d3 ("x86/vdso: Discard .note.gnu.property sections in vDSO") Signed-off-by: Bin Meng Reviewed-by: Tom Rini --- arch/x86/cpu/u-boot-64.lds | 1 + arch/x86/cpu/u-boot-spl.lds | 1 + arch/x86/cpu/u-boot.lds | 1 + 3 files changed, 3 insertions(+) diff --git a/arch/x86/cpu/u-boot-64.lds b/arch/x86/cpu/u-boot-64.lds index 98c7f8e9c5..ee0812aefb 100644 --- a/arch/x86/cpu/u-boot-64.lds +++ b/arch/x86/cpu/u-boot-64.lds @@ -107,4 +107,5 @@ SECTIONS /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } + /DISCARD/ : { *(.note.gnu.property) } } diff --git a/arch/x86/cpu/u-boot-spl.lds b/arch/x86/cpu/u-boot-spl.lds index 4a655bf9b5..346f60bdac 100644 --- a/arch/x86/cpu/u-boot-spl.lds +++ b/arch/x86/cpu/u-boot-spl.lds @@ -82,6 +82,7 @@ SECTIONS /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } + /DISCARD/ : { *(.note.gnu.property) } #if defined(CONFIG_SPL_X86_16BIT_INIT) || defined(CONFIG_TPL_X86_16BIT_INIT) /* diff --git a/arch/x86/cpu/u-boot.lds b/arch/x86/cpu/u-boot.lds index a283c290ee..22fde01e74 100644 --- a/arch/x86/cpu/u-boot.lds +++ b/arch/x86/cpu/u-boot.lds @@ -105,6 +105,7 @@ SECTIONS /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } + /DISCARD/ : { *(.note.gnu.property) } #ifdef CONFIG_X86_16BIT_INIT /* From b12f62374e83675bc65174c2b4b7e3c6c368aa78 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 6 May 2021 20:24:30 +0200 Subject: [PATCH 02/11] nvme: fix for big endian systems writel() and co. already include the endian swap; doing the swap twice is, er, unhelpful. Tested on a P4080DS, which boots perfectly fine off NVMe with this. Signed-off-by: David Lamparter Reviewed-by: Bin Meng --- 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 c61dab20c5..d554ec54cb 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -157,7 +157,7 @@ static u16 nvme_read_completion_status(struct nvme_queue *nvmeq, u16 index) invalidate_dcache_range(start, stop); - return le16_to_cpu(readw(&(nvmeq->cqes[index].status))); + return readw(&(nvmeq->cqes[index].status)); } /** @@ -221,7 +221,7 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, } if (result) - *result = le32_to_cpu(readl(&(nvmeq->cqes[head].result))); + *result = readl(&(nvmeq->cqes[head].result)); if (++head == nvmeq->q_depth) { head = 0; @@ -304,7 +304,7 @@ static int nvme_enable_ctrl(struct nvme_dev *dev) { dev->ctrl_config &= ~NVME_CC_SHN_MASK; dev->ctrl_config |= NVME_CC_ENABLE; - writel(cpu_to_le32(dev->ctrl_config), &dev->bar->cc); + writel(dev->ctrl_config, &dev->bar->cc); return nvme_wait_ready(dev, true); } @@ -313,7 +313,7 @@ static int nvme_disable_ctrl(struct nvme_dev *dev) { dev->ctrl_config &= ~NVME_CC_SHN_MASK; dev->ctrl_config &= ~NVME_CC_ENABLE; - writel(cpu_to_le32(dev->ctrl_config), &dev->bar->cc); + writel(dev->ctrl_config, &dev->bar->cc); return nvme_wait_ready(dev, false); } From 859b33c948945f7904f60a2c12a3792d356d51ad Mon Sep 17 00:00:00 2001 From: Wesley Sheng Date: Tue, 22 Jun 2021 11:34:21 +0800 Subject: [PATCH 03/11] nvme: Correct the prps per page calculation method Each prp is 8 bytes, calculate the number of prps per page should just divide page size by 8 there is no need to minus 1 Signed-off-by: Wesley Sheng Reviewed-by: Bin Meng --- drivers/nvme/nvme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index d554ec54cb..dc6c39ba10 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -81,7 +81,7 @@ static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2, u64 *prp_pool; int length = total_len; int i, nprps; - u32 prps_per_page = (page_size >> 3) - 1; + u32 prps_per_page = page_size >> 3; u32 num_pages; length -= (page_size - offset); From c3e52c71bb0e7e5e34fadafbfcc34bce9899c100 Mon Sep 17 00:00:00 2001 From: Wesley Sheng Date: Tue, 22 Jun 2021 11:34:43 +0800 Subject: [PATCH 04/11] nvme: Remove the redundant aqa value setting AQA (Admin Queue Attributes) register is a dword size with lower word of ASQS, and higher word of ACQS. The code set the variable aqa twice, but it is redundant. Signed-off-by: Wesley Sheng Reviewed-by: Bin Meng --- drivers/nvme/nvme.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index dc6c39ba10..424fe6d945 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -387,7 +387,6 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev) aqa = nvmeq->q_depth - 1; aqa |= aqa << 16; - aqa |= aqa << 16; dev->page_size = 1 << page_shift; From 84344258f23fb9ac5abe7383c909f8606ac767b6 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 22 Jun 2021 21:16:17 +0800 Subject: [PATCH 05/11] nvme: Move block dev creation from uclass post_probe() to driver probe() At present the block device creation happens in the NVMe uclass driver post_probe() phase. In preparation to support multiple namespaces, we should issue namespace identify before creating block devices but that touches the underlying hardware hence it is not appropriate to do such in the uclass driver post_probe(). Let's move it to driver probe() phase instead. Signed-off-by: Bin Meng --- drivers/nvme/nvme-uclass.c | 30 ------------------------------ drivers/nvme/nvme.c | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/drivers/nvme/nvme-uclass.c b/drivers/nvme/nvme-uclass.c index 277e31e1f3..610166d76e 100644 --- a/drivers/nvme/nvme-uclass.c +++ b/drivers/nvme/nvme-uclass.c @@ -5,39 +5,9 @@ */ #include -#include -#include #include -#include -#include "nvme.h" - -static int nvme_uclass_post_probe(struct udevice *udev) -{ - char name[20]; - struct udevice *ns_udev; - int i, ret; - struct nvme_dev *ndev = dev_get_priv(udev); - - /* Create a blk device for each namespace */ - for (i = 0; i < ndev->nn; i++) { - /* - * Encode the namespace id to the device name so that - * we can extract it when doing the probe. - */ - sprintf(name, "blk#%d", i); - - /* The real blksz and size will be set by nvme_blk_probe() */ - ret = blk_create_devicef(udev, "nvme-blk", name, IF_TYPE_NVME, - -1, 512, 0, &ns_udev); - if (ret) - return ret; - } - - return 0; -} UCLASS_DRIVER(nvme) = { .name = "nvme", .id = UCLASS_NVME, - .post_probe = nvme_uclass_post_probe, }; diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index 424fe6d945..afb1a7ea97 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -878,6 +878,24 @@ static int nvme_probe(struct udevice *udev) nvme_get_info_from_identify(ndev); + /* Create a blk device for each namespace */ + for (int i = 0; i < ndev->nn; i++) { + struct udevice *ns_udev; + char name[20]; + + /* + * Encode the namespace id to the device name so that + * we can extract it when doing the probe. + */ + sprintf(name, "blk#%d", i); + + /* The real blksz and size will be set by nvme_blk_probe() */ + ret = blk_create_devicef(udev, "nvme-blk", name, IF_TYPE_NVME, + -1, 512, 0, &ns_udev); + if (ret) + goto free_queue; + } + return 0; free_queue: From 478f7fc6a04ae81af550b56b31f18af24cf3f262 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 22 Jun 2021 21:16:18 +0800 Subject: [PATCH 06/11] nvme: Skip block device creation for inactive namespaces At present for each namespace there is a block device created for it. There is no issue if the number of supported namespaces reported from the NVMe device is only 1. Since QEMU commit 7f0f1acedf15 ("hw/block/nvme: support multiple namespaces"), the number of supported namespaces reported has been changed from 1 to 256, but not all of them are active namespaces. The actual active one depends on the QEMU command line parameters. A common case is that namespace 1 being active and all other 255 being inactive. If a namespace is inactive, the namespace identify command returns a zero filled data structure. We can use field NSZE (namespace size) to decide whether a block device should be created for it. Reported-by: Heinrich Schuchardt Signed-off-by: Bin Meng --- drivers/nvme/nvme.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index afb1a7ea97..acf337a58a 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -834,6 +834,7 @@ static int nvme_probe(struct udevice *udev) { int ret; struct nvme_dev *ndev = dev_get_priv(udev); + struct nvme_id_ns *id; ndev->instance = trailing_strtol(udev->name); @@ -879,10 +880,27 @@ static int nvme_probe(struct udevice *udev) nvme_get_info_from_identify(ndev); /* Create a blk device for each namespace */ + + id = memalign(ndev->page_size, sizeof(struct nvme_id_ns)); + if (!id) { + ret = -ENOMEM; + goto free_queue; + } + for (int i = 0; i < ndev->nn; i++) { struct udevice *ns_udev; char name[20]; + memset(id, 0, sizeof(*id)); + if (nvme_identify(ndev, i + 1, 0, (dma_addr_t)(long)id)) { + ret = -EIO; + goto free_id; + } + + /* skip inactive namespace */ + if (!id->nsze) + continue; + /* * Encode the namespace id to the device name so that * we can extract it when doing the probe. @@ -893,11 +911,14 @@ static int nvme_probe(struct udevice *udev) ret = blk_create_devicef(udev, "nvme-blk", name, IF_TYPE_NVME, -1, 512, 0, &ns_udev); if (ret) - goto free_queue; + goto free_id; } + free(id); return 0; +free_id: + free(id); free_queue: free((void *)ndev->queues); free_nvme: From 8c60d40d69fd834f67322bbcea06ef0adf9076e1 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 22 Jun 2021 21:16:19 +0800 Subject: [PATCH 07/11] nvme: Eliminate the offset of one during block dev creation At present there is an offset of one added during the creation of block device. This can be very confusing as we wanted to encode the namespace id in the block device name but namespae id cannot be zero. This changes to use the namespace id directly in the block device name, eliminating the offset of one effectively. Suggested-by: Heinrich Schuchardt Signed-off-by: Bin Meng --- drivers/nvme/nvme.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index acf337a58a..c5af4c08ae 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -708,7 +708,7 @@ static int nvme_blk_probe(struct udevice *udev) memset(ns, 0, sizeof(*ns)); ns->dev = ndev; /* extract the namespace id from the block device name */ - ns->ns_id = trailing_strtol(udev->name) + 1; + ns->ns_id = trailing_strtol(udev->name); if (nvme_identify(ndev, ns->ns_id, 0, (dma_addr_t)(long)id)) { free(id); return -EIO; @@ -887,12 +887,12 @@ static int nvme_probe(struct udevice *udev) goto free_queue; } - for (int i = 0; i < ndev->nn; i++) { + for (int i = 1; i <= ndev->nn; i++) { struct udevice *ns_udev; char name[20]; memset(id, 0, sizeof(*id)); - if (nvme_identify(ndev, i + 1, 0, (dma_addr_t)(long)id)) { + if (nvme_identify(ndev, i, 0, (dma_addr_t)(long)id)) { ret = -EIO; goto free_id; } From 757cc4b1b2725e445a94840aa6076f0f0d4f03b9 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 22 Jun 2021 21:16:20 +0800 Subject: [PATCH 08/11] nvme: Drop useless members of 'struct nvme_ns' mode_select_num_blocks and mode_select_block_len in 'struct nvme_ns' are not useful. Drop them. Signed-off-by: Bin Meng --- drivers/nvme/nvme.c | 4 +--- drivers/nvme/nvme.h | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index c5af4c08ae..d4b2860d67 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -718,11 +718,9 @@ static int nvme_blk_probe(struct udevice *udev) flbas = id->flbas & NVME_NS_FLBAS_LBA_MASK; ns->flbas = flbas; ns->lba_shift = id->lbaf[flbas].ds; - ns->mode_select_num_blocks = le64_to_cpu(id->nsze); - ns->mode_select_block_len = 1 << ns->lba_shift; list_add(&ns->list, &ndev->namespaces); - desc->lba = ns->mode_select_num_blocks; + desc->lba = le64_to_cpu(id->nsze); desc->log2blksz = ns->lba_shift; desc->blksz = 1 << ns->lba_shift; desc->bdev = udev; diff --git a/drivers/nvme/nvme.h b/drivers/nvme/nvme.h index aa4b3bac67..c6aae4da5d 100644 --- a/drivers/nvme/nvme.h +++ b/drivers/nvme/nvme.h @@ -633,8 +633,6 @@ struct nvme_ns { int devnum; int lba_shift; u8 flbas; - u64 mode_select_num_blocks; - u32 mode_select_block_len; }; #endif /* __DRIVER_NVME_H__ */ From 78d5f2011e2acf8e2f259d2886f1ceb4d789e0be Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 22 Jun 2021 21:16:21 +0800 Subject: [PATCH 09/11] nvme: Don't clear nvme blk device's priv space A udevice's priv space is cleared in alloc_priv() in the DM core. Don't do it again in its probe() routine. Signed-off-by: Bin Meng --- drivers/nvme/nvme.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index d4b2860d67..f6465ea7f4 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -705,7 +705,6 @@ static int nvme_blk_probe(struct udevice *udev) if (!id) return -ENOMEM; - memset(ns, 0, sizeof(*ns)); ns->dev = ndev; /* extract the namespace id from the block device name */ ns->ns_id = trailing_strtol(udev->name); From bf2a28356e1cd48597f50ad351d3e6806c468a56 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 22 Jun 2021 21:16:22 +0800 Subject: [PATCH 10/11] doc: develop: Convert README.nvme to reST This converts the existing README.nvme to reST, and puts it under the develop/driver-model/ directory. Signed-off-by: Bin Meng --- doc/develop/driver-model/index.rst | 1 + .../driver-model/nvme.rst} | 25 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) rename doc/{README.nvme => develop/driver-model/nvme.rst} (88%) diff --git a/doc/develop/driver-model/index.rst b/doc/develop/driver-model/index.rst index 10a76256b0..7366ef818c 100644 --- a/doc/develop/driver-model/index.rst +++ b/doc/develop/driver-model/index.rst @@ -19,6 +19,7 @@ subsystems i2c-howto livetree migration + nvme of-plat pci-info pmic-framework diff --git a/doc/README.nvme b/doc/develop/driver-model/nvme.rst similarity index 88% rename from doc/README.nvme rename to doc/develop/driver-model/nvme.rst index e8f9be149e..736c0a063d 100644 --- a/doc/README.nvme +++ b/doc/develop/driver-model/nvme.rst @@ -1,10 +1,12 @@ -# SPDX-License-Identifier: GPL-2.0+ -# -# Copyright (C) 2017 NXP Semiconductors -# Copyright (C) 2017 Bin Meng +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (C) 2017 NXP Semiconductors +.. Copyright (C) 2017 Bin Meng + +NVMe Support +============ What is NVMe -============ +------------ NVM Express (NVMe) is a register level interface that allows host software to communicate with a non-volatile memory subsystem. This interface is optimized @@ -48,6 +50,8 @@ identified. To list all of the NVMe hard disks, try: +.. code-block:: none + => nvme info Device 0: Vendor: 0x8086 Rev: 8DV10131 Prod: CVFT535600LS400BGN Type: Hard Disk @@ -55,10 +59,14 @@ To list all of the NVMe hard disks, try: and print out detailed information for controller and namespaces via: +.. code-block:: none + => nvme detail Raw block read/write to can be done via the 'nvme read/write' commands: +.. code-block:: none + => nvme read a0000000 0 11000 => tftp 80000000 /tftpboot/kernel.itb @@ -66,6 +74,8 @@ Raw block read/write to can be done via the 'nvme read/write' commands: Of course, file system command can be used on the NVMe hard disk as well: +.. code-block:: none + => fatls nvme 0:1 32376967 kernel.itb 22929408 100m @@ -81,4 +91,7 @@ QEMU supports NVMe emulation and we can test NVMe driver with QEMU x86 running U-Boot. Please see README.x86 for how to build u-boot.rom image for QEMU x86. Example command line to call QEMU x86 below with emulated NVMe device: -$ ./qemu-system-i386 -drive file=nvme.img,if=none,id=drv0 -device nvme,drive=drv0,serial=QEMUNVME0001 -bios u-boot.rom + +.. code-block:: bash + + $ ./qemu-system-i386 -drive file=nvme.img,if=none,id=drv0 -device nvme,drive=drv0,serial=QEMUNVME0001 -bios u-boot.rom From f68d5a66cd53a238d64d79cdd330b4dce17c7197 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 22 Jun 2021 21:16:23 +0800 Subject: [PATCH 11/11] MAINTAINERS: Add an entry for NVMe This was missed when NVMe support was initially brought to U-Boot back in 2017. Add an entry for it and list myself as the maintainer. Signed-off-by: Bin Meng Reviewed-by: Tom Rini --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2accd1fb83..81190f8f8f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -913,6 +913,14 @@ S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-nios.git F: arch/nios2/ +NVMe +M: Bin Meng +S: Maintained +F: drivers/nvme/ +F: cmd/nvme.c +F: include/nvme.h +F: doc/develop/driver-model/nvme.rst + ONENAND #M: Lukasz Majewski S: Orphaned (Since 2017-01)