0014-Add-native-NVMe-driver-based-on-SeaBIOS.patch 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. From 246a626a369fc3730c6b5c21982fd89ed19c6fe0 Mon Sep 17 00:00:00 2001
  2. From: Mate Kukri <km@mkukri.xyz>
  3. Date: Mon, 20 May 2024 11:43:35 +0100
  4. Subject: [PATCH 14/14] Add native NVMe driver based on SeaBIOS
  5. Tested to successfully boot Debian on QEMU and OptiPlex 3050.
  6. Signed-off-by: Mate Kukri <km@mkukri.xyz>
  7. ---
  8. Makefile.am | 2 +-
  9. grub-core/Makefile.core.def | 6 +
  10. grub-core/commands/nativedisk.c | 1 +
  11. grub-core/disk/nvme-int.h | 208 +++++++++
  12. grub-core/disk/nvme.c | 781 ++++++++++++++++++++++++++++++++
  13. include/grub/disk.h | 1 +
  14. 6 files changed, 998 insertions(+), 1 deletion(-)
  15. create mode 100644 grub-core/disk/nvme-int.h
  16. create mode 100644 grub-core/disk/nvme.c
  17. diff --git a/Makefile.am b/Makefile.am
  18. index 43635d5ff..2c86dbbf6 100644
  19. --- a/Makefile.am
  20. +++ b/Makefile.am
  21. @@ -434,7 +434,7 @@ if COND_i386_coreboot
  22. FS_PAYLOAD_MODULES ?= $(shell cat grub-core/fs.lst)
  23. default_payload.elf: grub-mkstandalone grub-mkimage FORCE
  24. test -f $@ && rm $@ || true
  25. - pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg
  26. + pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata nvme ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg
  27. endif
  28. endif
  29. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
  30. index 5c1af8682..02967d3ff 100644
  31. --- a/grub-core/Makefile.core.def
  32. +++ b/grub-core/Makefile.core.def
  33. @@ -2614,3 +2614,9 @@ module = {
  34. enable = efi;
  35. depends = part_gpt;
  36. };
  37. +
  38. +module = {
  39. + name = nvme;
  40. + common = disk/nvme.c;
  41. + enable = pci;
  42. +};
  43. diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c
  44. index 6806bff9c..fd68a513e 100644
  45. --- a/grub-core/commands/nativedisk.c
  46. +++ b/grub-core/commands/nativedisk.c
  47. @@ -78,6 +78,7 @@ get_uuid (const char *name, char **uuid, int getnative)
  48. case GRUB_DISK_DEVICE_ATA_ID:
  49. case GRUB_DISK_DEVICE_SCSI_ID:
  50. case GRUB_DISK_DEVICE_XEN:
  51. + case GRUB_DISK_DEVICE_NVME_ID:
  52. if (getnative)
  53. break;
  54. /* FALLTHROUGH */
  55. diff --git a/grub-core/disk/nvme-int.h b/grub-core/disk/nvme-int.h
  56. new file mode 100644
  57. index 000000000..1295b58aa
  58. --- /dev/null
  59. +++ b/grub-core/disk/nvme-int.h
  60. @@ -0,0 +1,208 @@
  61. +// NVMe datastructures and constants
  62. +//
  63. +// Copyright 2017 Amazon.com, Inc. or its affiliates.
  64. +//
  65. +// This file may be distributed under the terms of the GNU LGPLv3 license.
  66. +
  67. +#ifndef __NVME_INT_H
  68. +#define __NVME_INT_H
  69. +
  70. +#include <grub/types.h>
  71. +
  72. +/* Data structures */
  73. +
  74. +/* The register file of a NVMe host controller. This struct follows the naming
  75. + scheme in the NVMe specification. */
  76. +struct nvme_reg {
  77. + grub_uint64_t cap; /* controller capabilities */
  78. + grub_uint32_t vs; /* version */
  79. + grub_uint32_t intms; /* interrupt mask set */
  80. + grub_uint32_t intmc; /* interrupt mask clear */
  81. + grub_uint32_t cc; /* controller configuration */
  82. + grub_uint32_t _res0;
  83. + grub_uint32_t csts; /* controller status */
  84. + grub_uint32_t _res1;
  85. + grub_uint32_t aqa; /* admin queue attributes */
  86. + grub_uint64_t asq; /* admin submission queue base address */
  87. + grub_uint64_t acq; /* admin completion queue base address */
  88. +};
  89. +
  90. +/* Submission queue entry */
  91. +struct nvme_sqe {
  92. + union {
  93. + grub_uint32_t dword[16];
  94. + struct {
  95. + grub_uint32_t cdw0; /* Command DWORD 0 */
  96. + grub_uint32_t nsid; /* Namespace ID */
  97. + grub_uint64_t _res0;
  98. + grub_uint64_t mptr; /* metadata ptr */
  99. +
  100. + grub_uint64_t dptr_prp1;
  101. + grub_uint64_t dptr_prp2;
  102. + };
  103. + };
  104. +};
  105. +
  106. +/* Completion queue entry */
  107. +struct nvme_cqe {
  108. + union {
  109. + grub_uint32_t dword[4];
  110. + struct {
  111. + grub_uint32_t cdw0;
  112. + grub_uint32_t _res0;
  113. + grub_uint16_t sq_head;
  114. + grub_uint16_t sq_id;
  115. + grub_uint16_t cid;
  116. + grub_uint16_t status;
  117. + };
  118. + };
  119. +};
  120. +
  121. +/* The common part of every submission or completion queue. */
  122. +struct nvme_queue {
  123. + grub_uint32_t *dbl; /* doorbell */
  124. + grub_uint16_t mask; /* length - 1 */
  125. +};
  126. +
  127. +struct nvme_cq {
  128. + struct nvme_queue common;
  129. + struct nvme_cqe *cqe;
  130. +
  131. + /* We have read upto (but not including) this entry in the queue. */
  132. + grub_uint16_t head;
  133. +
  134. + /* The current phase bit the controller uses to indicate that it has written
  135. + a new entry. This is inverted after each wrap. */
  136. + unsigned phase : 1;
  137. +};
  138. +
  139. +struct nvme_sq {
  140. + struct nvme_queue common;
  141. + struct nvme_sqe *sqe;
  142. +
  143. + /* Corresponding completion queue. We only support a single SQ per CQ. */
  144. + struct nvme_cq *cq;
  145. +
  146. + /* The last entry the controller has fetched. */
  147. + grub_uint16_t head;
  148. +
  149. + /* The last value we have written to the tail doorbell. */
  150. + grub_uint16_t tail;
  151. +};
  152. +
  153. +struct nvme_ctrl {
  154. + grub_pci_device_t pci;
  155. + struct nvme_reg volatile *reg;
  156. +
  157. + grub_uint32_t ctrlnum;
  158. +
  159. + grub_uint32_t doorbell_stride; /* in bytes */
  160. +
  161. + struct nvme_sq admin_sq;
  162. + struct nvme_cq admin_cq;
  163. +
  164. + grub_uint32_t ns_count;
  165. +
  166. + struct nvme_sq io_sq;
  167. + struct nvme_cq io_cq;
  168. +};
  169. +
  170. +struct nvme_namespace {
  171. + struct nvme_namespace *next;
  172. + struct nvme_namespace **prev;
  173. +
  174. + char *devname;
  175. +
  176. + grub_uint32_t nsnum;
  177. +
  178. + struct nvme_ctrl *ctrl;
  179. +
  180. + grub_uint32_t ns_id;
  181. +
  182. + grub_uint64_t lba_count; /* The total amount of sectors. */
  183. +
  184. + grub_uint32_t block_size;
  185. + grub_uint32_t metadata_size;
  186. + grub_uint32_t max_req_size;
  187. +};
  188. +
  189. +/* Data structures for NVMe admin identify commands */
  190. +
  191. +struct nvme_identify_ctrl {
  192. + grub_uint16_t vid;
  193. + grub_uint16_t ssvid;
  194. + char sn[20];
  195. + char mn[40];
  196. + char fr[8];
  197. +
  198. + grub_uint8_t rab;
  199. + grub_uint8_t ieee[3];
  200. + grub_uint8_t cmic;
  201. + grub_uint8_t mdts;
  202. +
  203. + char _boring[516 - 78];
  204. +
  205. + grub_uint32_t nn; /* number of namespaces */
  206. +};
  207. +
  208. +struct nvme_identify_ns_list {
  209. + grub_uint32_t ns_id[1024];
  210. +};
  211. +
  212. +struct nvme_lba_format {
  213. + grub_uint16_t ms;
  214. + grub_uint8_t lbads;
  215. + grub_uint8_t rp;
  216. +};
  217. +
  218. +struct nvme_identify_ns {
  219. + grub_uint64_t nsze;
  220. + grub_uint64_t ncap;
  221. + grub_uint64_t nuse;
  222. + grub_uint8_t nsfeat;
  223. + grub_uint8_t nlbaf;
  224. + grub_uint8_t flbas;
  225. +
  226. + char _boring[128 - 27];
  227. +
  228. + struct nvme_lba_format lbaf[16];
  229. +};
  230. +
  231. +union nvme_identify {
  232. + struct nvme_identify_ns ns;
  233. + struct nvme_identify_ctrl ctrl;
  234. + struct nvme_identify_ns_list ns_list;
  235. +};
  236. +
  237. +/* NVMe constants */
  238. +
  239. +#define NVME_CAP_CSS_NVME (1ULL << 37)
  240. +
  241. +#define NVME_CSTS_FATAL (1U << 1)
  242. +#define NVME_CSTS_RDY (1U << 0)
  243. +
  244. +#define NVME_CC_EN (1U << 0)
  245. +
  246. +#define NVME_SQE_OPC_ADMIN_CREATE_IO_SQ 1U
  247. +#define NVME_SQE_OPC_ADMIN_CREATE_IO_CQ 5U
  248. +#define NVME_SQE_OPC_ADMIN_IDENTIFY 6U
  249. +
  250. +#define NVME_SQE_OPC_IO_WRITE 1U
  251. +#define NVME_SQE_OPC_IO_READ 2U
  252. +
  253. +#define NVME_ADMIN_IDENTIFY_CNS_ID_NS 0U
  254. +#define NVME_ADMIN_IDENTIFY_CNS_ID_CTRL 1U
  255. +#define NVME_ADMIN_IDENTIFY_CNS_GET_NS_LIST 2U
  256. +
  257. +#define NVME_CQE_DW3_P (1U << 16)
  258. +
  259. +#define NVME_PAGE_SIZE 4096
  260. +#define NVME_PAGE_MASK ~(NVME_PAGE_SIZE - 1)
  261. +
  262. +/* Length for the queue entries. */
  263. +#define NVME_SQE_SIZE_LOG 6
  264. +#define NVME_CQE_SIZE_LOG 4
  265. +
  266. +#endif
  267. +
  268. +/* EOF */
  269. diff --git a/grub-core/disk/nvme.c b/grub-core/disk/nvme.c
  270. new file mode 100644
  271. index 000000000..093237c70
  272. --- /dev/null
  273. +++ b/grub-core/disk/nvme.c
  274. @@ -0,0 +1,781 @@
  275. +// Low level NVMe disk access
  276. +//
  277. +// Based on SeaBIOS NVMe driver - Copyright 2017 Amazon.com, Inc. or its affiliates.
  278. +// Port to GRUB2 done by Mate Kukri
  279. +//
  280. +// This file may be distributed under the terms of the GNU LGPLv3 license.
  281. +
  282. +#include <grub/disk.h>
  283. +#include <grub/dl.h>
  284. +#include <grub/pci.h>
  285. +#include "nvme-int.h"
  286. +
  287. +GRUB_MOD_LICENSE ("GPLv3"); /* LGPLv3 in reality but it is GPLv3 compatible */
  288. +
  289. +static grub_uint32_t grub_nvme_ctrlcnt;
  290. +static grub_uint32_t grub_nvme_nscnt;
  291. +
  292. +static struct nvme_namespace *grub_nvme_namespaces;
  293. +
  294. +// Page aligned "dma bounce buffer" of size NVME_PAGE_SIZE
  295. +static void *nvme_dma_buffer;
  296. +
  297. +static void *
  298. +zalloc_page_aligned(grub_uint32_t size)
  299. +{
  300. + void *res = grub_memalign(NVME_PAGE_SIZE, size);
  301. + if (res) grub_memset(res, 0, size);
  302. + return res;
  303. +}
  304. +
  305. +static void
  306. +nvme_init_queue_common(struct nvme_ctrl *ctrl, struct nvme_queue *q, grub_uint16_t q_idx,
  307. + grub_uint16_t length)
  308. +{
  309. + grub_memset(q, 0, sizeof(*q));
  310. + q->dbl = (grub_uint32_t *)((char *)ctrl->reg + 0x1000 + q_idx * ctrl->doorbell_stride);
  311. + grub_dprintf("nvme", " q %p q_idx %u dbl %p\n", q, q_idx, q->dbl);
  312. + q->mask = length - 1;
  313. +}
  314. +
  315. +static int
  316. +nvme_init_sq(struct nvme_ctrl *ctrl, struct nvme_sq *sq, grub_uint16_t q_idx, grub_uint16_t length,
  317. + struct nvme_cq *cq)
  318. +{
  319. + nvme_init_queue_common(ctrl, &sq->common, q_idx, length);
  320. + sq->sqe = zalloc_page_aligned(sizeof(*sq->sqe) * length);
  321. +
  322. + if (!sq->sqe) {
  323. + return -1;
  324. + }
  325. +
  326. + grub_dprintf("nvme", "sq %p q_idx %u sqe %p\n", sq, q_idx, sq->sqe);
  327. + sq->cq = cq;
  328. + sq->head = 0;
  329. + sq->tail = 0;
  330. +
  331. + return 0;
  332. +}
  333. +
  334. +static int
  335. +nvme_init_cq(struct nvme_ctrl *ctrl, struct nvme_cq *cq, grub_uint16_t q_idx, grub_uint16_t length)
  336. +{
  337. + nvme_init_queue_common(ctrl, &cq->common, q_idx, length);
  338. + cq->cqe = zalloc_page_aligned(sizeof(*cq->cqe) * length);
  339. + if (!cq->cqe) {
  340. + return -1;
  341. + }
  342. +
  343. + cq->head = 0;
  344. +
  345. + /* All CQE phase bits are initialized to zero. This means initially we wait
  346. + for the host controller to set these to 1. */
  347. + cq->phase = 1;
  348. +
  349. + return 0;
  350. +}
  351. +
  352. +static int
  353. +nvme_poll_cq(struct nvme_cq *cq)
  354. +{
  355. + grub_uint32_t dw3 = *(volatile grub_uint32_t *) &cq->cqe[cq->head].dword[3];
  356. + return (!!(dw3 & NVME_CQE_DW3_P) == cq->phase);
  357. +}
  358. +
  359. +static int
  360. +nvme_is_cqe_success(struct nvme_cqe const *cqe)
  361. +{
  362. + return ((cqe->status >> 1) & 0xFF) == 0;
  363. +}
  364. +
  365. +static struct nvme_cqe
  366. +nvme_error_cqe(void)
  367. +{
  368. + struct nvme_cqe r;
  369. +
  370. + /* 0xFF is a vendor specific status code != success. Should be okay for
  371. + indicating failure. */
  372. + grub_memset(&r, 0xFF, sizeof(r));
  373. + return r;
  374. +}
  375. +
  376. +static struct nvme_cqe
  377. +nvme_consume_cqe(struct nvme_sq *sq)
  378. +{
  379. + struct nvme_cq *cq = sq->cq;
  380. +
  381. + if (!nvme_poll_cq(cq)) {
  382. + /* Cannot consume a completion queue entry, if there is none ready. */
  383. + return nvme_error_cqe();
  384. + }
  385. +
  386. + struct nvme_cqe *cqe = &cq->cqe[cq->head];
  387. + grub_uint16_t cq_next_head = (cq->head + 1) & cq->common.mask;
  388. + grub_dprintf("nvme", "cq %p head %u -> %u\n", cq, cq->head, cq_next_head);
  389. + if (cq_next_head < cq->head) {
  390. + grub_dprintf("nvme", "cq %p wrap\n", cq);
  391. + cq->phase = ~cq->phase;
  392. + }
  393. + cq->head = cq_next_head;
  394. +
  395. + /* Update the submission queue head. */
  396. + if (cqe->sq_head != sq->head) {
  397. + sq->head = cqe->sq_head;
  398. + grub_dprintf("nvme", "sq %p advanced to %u\n", sq, cqe->sq_head);
  399. + }
  400. +
  401. + /* Tell the controller that we consumed the completion. */
  402. + *(volatile grub_uint32_t *) cq->common.dbl = cq->head;
  403. +
  404. + return *cqe;
  405. +}
  406. +
  407. +static struct nvme_cqe
  408. +nvme_wait(struct nvme_sq *sq)
  409. +{
  410. + // static const unsigned nvme_timeout = 5000 /* ms */;
  411. + // grub_uint32_t to = timer_calc(nvme_timeout);
  412. + while (!nvme_poll_cq(sq->cq)) {
  413. + /* FIXME
  414. + yield();
  415. +
  416. + if (timer_check(to)) {
  417. + warn_timeout();
  418. + return nvme_error_cqe();
  419. + }*/
  420. + }
  421. +
  422. + return nvme_consume_cqe(sq);
  423. +}
  424. +
  425. +/* Returns the next submission queue entry (or NULL if the queue is full). It
  426. + also fills out Command Dword 0 and clears the rest. */
  427. +static struct nvme_sqe *
  428. +nvme_get_next_sqe(struct nvme_sq *sq, grub_uint8_t opc, void *metadata, void *data, void *data2)
  429. +{
  430. + if (((sq->head + 1) & sq->common.mask) == sq->tail) {
  431. + grub_dprintf("nvme", "submission queue is full\n");
  432. + return NULL;
  433. + }
  434. +
  435. + struct nvme_sqe *sqe = &sq->sqe[sq->tail];
  436. + grub_dprintf("nvme", "sq %p next_sqe %u\n", sq, sq->tail);
  437. +
  438. + grub_memset(sqe, 0, sizeof(*sqe));
  439. + sqe->cdw0 = opc | (sq->tail << 16 /* CID */);
  440. + sqe->mptr = (grub_uint32_t)metadata;
  441. + sqe->dptr_prp1 = (grub_uint32_t)data;
  442. + sqe->dptr_prp2 = (grub_uint32_t)data2;
  443. +
  444. + return sqe;
  445. +}
  446. +
  447. +/* Call this after you've filled out an sqe that you've got from nvme_get_next_sqe. */
  448. +static void
  449. +nvme_commit_sqe(struct nvme_sq *sq)
  450. +{
  451. + grub_dprintf("nvme", "sq %p commit_sqe %u\n", sq, sq->tail);
  452. + sq->tail = (sq->tail + 1) & sq->common.mask;
  453. + *(volatile grub_uint32_t *) sq->common.dbl = sq->tail;
  454. +}
  455. +
  456. +/* Perform an identify command on the admin queue and return the resulting
  457. + buffer. This may be a NULL pointer, if something failed. This function
  458. + cannot be used after initialization, because it uses buffers in tmp zone. */
  459. +static union nvme_identify *
  460. +nvme_admin_identify(struct nvme_ctrl *ctrl, grub_uint8_t cns, grub_uint32_t nsid)
  461. +{
  462. + union nvme_identify *identify_buf = zalloc_page_aligned(4096);
  463. + if (!identify_buf)
  464. + return NULL;
  465. +
  466. + struct nvme_sqe *cmd_identify;
  467. + cmd_identify = nvme_get_next_sqe(&ctrl->admin_sq,
  468. + NVME_SQE_OPC_ADMIN_IDENTIFY, NULL,
  469. + identify_buf, NULL);
  470. + if (!cmd_identify)
  471. + goto error;
  472. +
  473. + cmd_identify->nsid = nsid;
  474. + cmd_identify->dword[10] = cns;
  475. +
  476. + nvme_commit_sqe(&ctrl->admin_sq);
  477. +
  478. + struct nvme_cqe cqe = nvme_wait(&ctrl->admin_sq);
  479. +
  480. + if (!nvme_is_cqe_success(&cqe)) {
  481. + goto error;
  482. + }
  483. +
  484. + return identify_buf;
  485. + error:
  486. + grub_free(identify_buf);
  487. + return NULL;
  488. +}
  489. +
  490. +static struct nvme_identify_ctrl *
  491. +nvme_admin_identify_ctrl(struct nvme_ctrl *ctrl)
  492. +{
  493. + return &nvme_admin_identify(ctrl, NVME_ADMIN_IDENTIFY_CNS_ID_CTRL, 0)->ctrl;
  494. +}
  495. +
  496. +static struct nvme_identify_ns *
  497. +nvme_admin_identify_ns(struct nvme_ctrl *ctrl, grub_uint32_t ns_id)
  498. +{
  499. + return &nvme_admin_identify(ctrl, NVME_ADMIN_IDENTIFY_CNS_ID_NS,
  500. + ns_id)->ns;
  501. +}
  502. +
  503. +static void
  504. +nvme_probe_ns(struct nvme_ctrl *ctrl, grub_uint32_t ns_idx, grub_uint8_t mdts)
  505. +{
  506. + grub_uint32_t ns_id = ns_idx + 1;
  507. +
  508. + struct nvme_identify_ns *id = nvme_admin_identify_ns(ctrl, ns_id);
  509. + if (!id) {
  510. + grub_dprintf("nvme", "NVMe couldn't identify namespace %u.\n", ns_id);
  511. + goto free_buffer;
  512. + }
  513. +
  514. + grub_uint8_t current_lba_format = id->flbas & 0xF;
  515. + if (current_lba_format > id->nlbaf) {
  516. + grub_dprintf("nvme", "NVMe NS %u: current LBA format %u is beyond what the "
  517. + " namespace supports (%u)?\n",
  518. + ns_id, current_lba_format, id->nlbaf + 1);
  519. + goto free_buffer;
  520. + }
  521. +
  522. + if (!id->nsze) {
  523. + grub_dprintf("nvme", "NVMe NS %u is inactive.\n", ns_id);
  524. + goto free_buffer;
  525. + }
  526. +
  527. + if (!nvme_dma_buffer) {
  528. + nvme_dma_buffer = zalloc_page_aligned(NVME_PAGE_SIZE);
  529. + if (!nvme_dma_buffer) {
  530. + goto free_buffer;
  531. + }
  532. + }
  533. +
  534. + struct nvme_namespace *ns = grub_malloc(sizeof(*ns));
  535. + if (!ns) {
  536. + goto free_buffer;
  537. + }
  538. + grub_memset(ns, 0, sizeof(*ns));
  539. + ns->ctrl = ctrl;
  540. + ns->ns_id = ns_id;
  541. + ns->lba_count = id->nsze;
  542. +
  543. + struct nvme_lba_format *fmt = &id->lbaf[current_lba_format];
  544. +
  545. + ns->block_size = 1U << fmt->lbads;
  546. + ns->metadata_size = fmt->ms;
  547. +
  548. + if (ns->block_size > NVME_PAGE_SIZE) {
  549. + /* If we see devices that trigger this path, we need to increase our
  550. + buffer size. */
  551. + grub_free(ns);
  552. + goto free_buffer;
  553. + }
  554. +
  555. + if (mdts) {
  556. + ns->max_req_size = ((1U << mdts) * NVME_PAGE_SIZE) / ns->block_size;
  557. + grub_dprintf("nvme", "NVME NS %u max request size: %d sectors\n",
  558. + ns_id, ns->max_req_size);
  559. + } else {
  560. + ns->max_req_size = -1U;
  561. + }
  562. +
  563. + ns->devname = grub_xasprintf("nvme%un%u", ctrl->ctrlnum, ns_id);
  564. + ns->nsnum = grub_nvme_nscnt++;
  565. +
  566. + grub_list_push (GRUB_AS_LIST_P (&grub_nvme_namespaces), GRUB_AS_LIST (ns));
  567. +
  568. +free_buffer:
  569. + grub_free(id);
  570. +}
  571. +
  572. +
  573. +/* Release memory allocated for a completion queue */
  574. +static void
  575. +nvme_destroy_cq(struct nvme_cq *cq)
  576. +{
  577. + grub_free(cq->cqe);
  578. + cq->cqe = NULL;
  579. +}
  580. +
  581. +/* Release memory allocated for a submission queue */
  582. +static void
  583. +nvme_destroy_sq(struct nvme_sq *sq)
  584. +{
  585. + grub_free(sq->sqe);
  586. + sq->sqe = NULL;
  587. +}
  588. +
  589. +/* Returns 0 on success. */
  590. +static int
  591. +nvme_create_io_cq(struct nvme_ctrl *ctrl, struct nvme_cq *cq, grub_uint16_t q_idx)
  592. +{
  593. + int rc;
  594. + struct nvme_sqe *cmd_create_cq;
  595. + grub_uint32_t length = 1 + (ctrl->reg->cap & 0xffff);
  596. + if (length > NVME_PAGE_SIZE / sizeof(struct nvme_cqe))
  597. + length = NVME_PAGE_SIZE / sizeof(struct nvme_cqe);
  598. +
  599. + rc = nvme_init_cq(ctrl, cq, q_idx, length);
  600. + if (rc) {
  601. + goto err;
  602. + }
  603. +
  604. + cmd_create_cq = nvme_get_next_sqe(&ctrl->admin_sq,
  605. + NVME_SQE_OPC_ADMIN_CREATE_IO_CQ, NULL,
  606. + cq->cqe, NULL);
  607. + if (!cmd_create_cq) {
  608. + goto err_destroy_cq;
  609. + }
  610. +
  611. + cmd_create_cq->dword[10] = (cq->common.mask << 16) | (q_idx >> 1);
  612. + cmd_create_cq->dword[11] = 1 /* physically contiguous */;
  613. +
  614. + nvme_commit_sqe(&ctrl->admin_sq);
  615. +
  616. + struct nvme_cqe cqe = nvme_wait(&ctrl->admin_sq);
  617. +
  618. + if (!nvme_is_cqe_success(&cqe)) {
  619. + grub_dprintf("nvme", "create io cq failed: %08x %08x %08x %08x\n",
  620. + cqe.dword[0], cqe.dword[1], cqe.dword[2], cqe.dword[3]);
  621. +
  622. + goto err_destroy_cq;
  623. + }
  624. +
  625. + return 0;
  626. +
  627. +err_destroy_cq:
  628. + nvme_destroy_cq(cq);
  629. +err:
  630. + return -1;
  631. +}
  632. +
  633. +/* Returns 0 on success. */
  634. +static int
  635. +nvme_create_io_sq(struct nvme_ctrl *ctrl, struct nvme_sq *sq, grub_uint16_t q_idx, struct nvme_cq *cq)
  636. +{
  637. + int rc;
  638. + struct nvme_sqe *cmd_create_sq;
  639. + grub_uint32_t length = 1 + (ctrl->reg->cap & 0xffff);
  640. + if (length > NVME_PAGE_SIZE / sizeof(struct nvme_cqe))
  641. + length = NVME_PAGE_SIZE / sizeof(struct nvme_cqe);
  642. +
  643. + rc = nvme_init_sq(ctrl, sq, q_idx, length, cq);
  644. + if (rc) {
  645. + goto err;
  646. + }
  647. +
  648. + cmd_create_sq = nvme_get_next_sqe(&ctrl->admin_sq,
  649. + NVME_SQE_OPC_ADMIN_CREATE_IO_SQ, NULL,
  650. + sq->sqe, NULL);
  651. + if (!cmd_create_sq) {
  652. + goto err_destroy_sq;
  653. + }
  654. +
  655. + cmd_create_sq->dword[10] = (sq->common.mask << 16) | (q_idx >> 1);
  656. + cmd_create_sq->dword[11] = (q_idx >> 1) << 16 | 1 /* contiguous */;
  657. + grub_dprintf("nvme", "sq %p create dword10 %08x dword11 %08x\n", sq,
  658. + cmd_create_sq->dword[10], cmd_create_sq->dword[11]);
  659. +
  660. + nvme_commit_sqe(&ctrl->admin_sq);
  661. +
  662. + struct nvme_cqe cqe = nvme_wait(&ctrl->admin_sq);
  663. +
  664. + if (!nvme_is_cqe_success(&cqe)) {
  665. + grub_dprintf("nvme", "create io sq failed: %08x %08x %08x %08x\n",
  666. + cqe.dword[0], cqe.dword[1], cqe.dword[2], cqe.dword[3]);
  667. + goto err_destroy_sq;
  668. + }
  669. +
  670. + return 0;
  671. +
  672. +err_destroy_sq:
  673. + nvme_destroy_sq(sq);
  674. +err:
  675. + return -1;
  676. +}
  677. +
  678. +/* Reads count sectors into buf. The buffer cannot cross page boundaries. */
  679. +static int
  680. +nvme_io_xfer(struct nvme_namespace *ns, grub_uint64_t lba, void *prp1, void *prp2,
  681. + grub_uint16_t count, int write)
  682. +{
  683. + if (((grub_uint32_t)prp1 & 0x3) || ((grub_uint32_t)prp2 & 0x3)) {
  684. + /* Buffer is misaligned */
  685. + return -1;
  686. + }
  687. +
  688. + struct nvme_sqe *io_read = nvme_get_next_sqe(&ns->ctrl->io_sq,
  689. + write ? NVME_SQE_OPC_IO_WRITE
  690. + : NVME_SQE_OPC_IO_READ,
  691. + NULL, prp1, prp2);
  692. + io_read->nsid = ns->ns_id;
  693. + io_read->dword[10] = (grub_uint32_t)lba;
  694. + io_read->dword[11] = (grub_uint32_t)(lba >> 32);
  695. + io_read->dword[12] = (1U << 31 /* limited retry */) | (count - 1);
  696. +
  697. + nvme_commit_sqe(&ns->ctrl->io_sq);
  698. +
  699. + struct nvme_cqe cqe = nvme_wait(&ns->ctrl->io_sq);
  700. +
  701. + if (!nvme_is_cqe_success(&cqe)) {
  702. + grub_dprintf("nvme", "read io: %08x %08x %08x %08x\n",
  703. + cqe.dword[0], cqe.dword[1], cqe.dword[2], cqe.dword[3]);
  704. +
  705. + return -1;
  706. + }
  707. +
  708. + grub_dprintf("nvme", "ns %u %s lba %llu+%u\n", ns->ns_id, write ? "write" : "read",
  709. + lba, count);
  710. + return count;
  711. +}
  712. +
  713. +// Transfer up to one page of data using the internal dma bounce buffer
  714. +static int
  715. +nvme_bounce_xfer(struct nvme_namespace *ns, grub_uint64_t lba, void *buf, grub_uint16_t count,
  716. + int write)
  717. +{
  718. + grub_uint16_t const max_blocks = NVME_PAGE_SIZE / ns->block_size;
  719. + grub_uint16_t blocks = count < max_blocks ? count : max_blocks;
  720. +
  721. + if (write)
  722. + grub_memcpy(nvme_dma_buffer, buf, blocks * ns->block_size);
  723. +
  724. + int res = nvme_io_xfer(ns, lba, nvme_dma_buffer, NULL, blocks, write);
  725. +
  726. + if (!write && res >= 0)
  727. + grub_memcpy(buf, nvme_dma_buffer, res * ns->block_size);
  728. +
  729. + return res;
  730. +}
  731. +
  732. +#define NVME_MAX_PRPL_ENTRIES 15 /* Allows requests up to 64kb */
  733. +
  734. +// Transfer data using page list (if applicable)
  735. +static int
  736. +nvme_prpl_xfer(struct nvme_namespace *ns, grub_uint64_t lba, void *buf, grub_uint16_t count,
  737. + int write)
  738. +{
  739. + grub_uint32_t base = (long)buf;
  740. + grub_int32_t size;
  741. +
  742. + if (count > ns->max_req_size)
  743. + count = ns->max_req_size;
  744. +
  745. + size = count * ns->block_size;
  746. + /* Special case for transfers that fit into PRP1, but are unaligned */
  747. + if (((size + (base & ~NVME_PAGE_MASK)) <= NVME_PAGE_SIZE))
  748. + goto single;
  749. +
  750. + /* Every request has to be page aligned */
  751. + if (base & ~NVME_PAGE_MASK)
  752. + goto bounce;
  753. +
  754. + /* Make sure a full block fits into the last chunk */
  755. + if (size & (ns->block_size - 1ULL))
  756. + goto bounce;
  757. +
  758. + /* Build PRP list if we need to describe more than 2 pages */
  759. + if ((ns->block_size * count) > (NVME_PAGE_SIZE * 2)) {
  760. + grub_uint32_t prpl_len = 0;
  761. + grub_uint64_t *prpl = nvme_dma_buffer;
  762. + int first_page = 1;
  763. + for (; size > 0; base += NVME_PAGE_SIZE, size -= NVME_PAGE_SIZE) {
  764. + if (first_page) {
  765. + /* First page is special */
  766. + first_page = 0;
  767. + continue;
  768. + }
  769. + if (prpl_len >= NVME_MAX_PRPL_ENTRIES)
  770. + goto bounce;
  771. + prpl[prpl_len++] = base;
  772. + }
  773. + return nvme_io_xfer(ns, lba, buf, prpl, count, write);
  774. + }
  775. +
  776. + /* Directly embed the 2nd page if we only need 2 pages */
  777. + if ((ns->block_size * count) > NVME_PAGE_SIZE)
  778. + return nvme_io_xfer(ns, lba, buf, (char *) buf + NVME_PAGE_SIZE, count, write);
  779. +
  780. +single:
  781. + /* One page is enough, don't expose anything else */
  782. + return nvme_io_xfer(ns, lba, buf, NULL, count, write);
  783. +
  784. +bounce:
  785. + /* Use bounce buffer to make transfer */
  786. + return nvme_bounce_xfer(ns, lba, buf, count, write);
  787. +}
  788. +
  789. +static int
  790. +nvme_create_io_queues(struct nvme_ctrl *ctrl)
  791. +{
  792. + if (nvme_create_io_cq(ctrl, &ctrl->io_cq, 3))
  793. + goto err;
  794. +
  795. + if (nvme_create_io_sq(ctrl, &ctrl->io_sq, 2, &ctrl->io_cq))
  796. + goto err_free_cq;
  797. +
  798. + return 0;
  799. +
  800. + err_free_cq:
  801. + nvme_destroy_cq(&ctrl->io_cq);
  802. + err:
  803. + return -1;
  804. +}
  805. +
  806. +/* Waits for CSTS.RDY to match rdy. Returns 0 on success. */
  807. +static int
  808. +nvme_wait_csts_rdy(struct nvme_ctrl *ctrl, unsigned rdy)
  809. +{
  810. + // grub_uint32_t const max_to = 500 /* ms */ * ((ctrl->reg->cap >> 24) & 0xFFU);
  811. + // grub_uint32_t to = timer_calc(max_to);
  812. + grub_uint32_t csts;
  813. +
  814. + while (rdy != ((csts = ctrl->reg->csts) & NVME_CSTS_RDY)) {
  815. + // FIXME
  816. + //yield();
  817. +
  818. + if (csts & NVME_CSTS_FATAL) {
  819. + grub_dprintf("nvme", "NVMe fatal error during controller shutdown\n");
  820. + return -1;
  821. + }
  822. +
  823. + /*
  824. + if (timer_check(to)) {
  825. + warn_timeout();
  826. + return -1;
  827. + }*/
  828. + }
  829. +
  830. + return 0;
  831. +}
  832. +
  833. +/* Returns 0 on success. */
  834. +static int grub_nvme_controller_enable(struct nvme_ctrl *ctrl)
  835. +{
  836. + grub_pci_address_t addr;
  837. + int rc;
  838. +
  839. + addr = grub_pci_make_address (ctrl->pci, GRUB_PCI_REG_COMMAND);
  840. + grub_pci_write_word (addr, grub_pci_read_word (addr) | GRUB_PCI_COMMAND_BUS_MASTER);
  841. +
  842. + /* Turn the controller off. */
  843. + ctrl->reg->cc = 0;
  844. + if (nvme_wait_csts_rdy(ctrl, 0)) {
  845. + grub_dprintf("nvme", "NVMe fatal error during controller shutdown\n");
  846. + return -1;
  847. + }
  848. +
  849. + ctrl->doorbell_stride = 4U << ((ctrl->reg->cap >> 32) & 0xF);
  850. +
  851. + rc = nvme_init_cq(ctrl, &ctrl->admin_cq, 1,
  852. + NVME_PAGE_SIZE / sizeof(struct nvme_cqe));
  853. + if (rc) {
  854. + return -1;
  855. + }
  856. +
  857. + rc = nvme_init_sq(ctrl, &ctrl->admin_sq, 0,
  858. + NVME_PAGE_SIZE / sizeof(struct nvme_sqe), &ctrl->admin_cq);
  859. + if (rc) {
  860. + goto err_destroy_admin_cq;
  861. + }
  862. +
  863. + ctrl->reg->aqa = ctrl->admin_cq.common.mask << 16
  864. + | ctrl->admin_sq.common.mask;
  865. +
  866. + ctrl->reg->asq = (grub_uint32_t)ctrl->admin_sq.sqe;
  867. + ctrl->reg->acq = (grub_uint32_t)ctrl->admin_cq.cqe;
  868. +
  869. + grub_dprintf("nvme", " admin submission queue: %p\n", ctrl->admin_sq.sqe);
  870. + grub_dprintf("nvme", " admin completion queue: %p\n", ctrl->admin_cq.cqe);
  871. +
  872. + ctrl->reg->cc = NVME_CC_EN | (NVME_CQE_SIZE_LOG << 20)
  873. + | (NVME_SQE_SIZE_LOG << 16 /* IOSQES */);
  874. +
  875. + if (nvme_wait_csts_rdy(ctrl, 1)) {
  876. + grub_dprintf("nvme", "NVMe fatal error while enabling controller\n");
  877. + goto err_destroy_admin_sq;
  878. + }
  879. +
  880. + /* The admin queue is set up and the controller is ready. Let's figure out
  881. + what namespaces we have. */
  882. +
  883. + struct nvme_identify_ctrl *identify = nvme_admin_identify_ctrl(ctrl);
  884. +
  885. + if (!identify) {
  886. + grub_dprintf("nvme", "NVMe couldn't identify controller.\n");
  887. + goto err_destroy_admin_sq;
  888. + }
  889. +
  890. + grub_dprintf("nvme", "NVMe has %u namespace%s.\n",
  891. + identify->nn, (identify->nn == 1) ? "" : "s");
  892. +
  893. + ctrl->ns_count = identify->nn;
  894. + grub_uint8_t mdts = identify->mdts;
  895. + grub_free(identify);
  896. +
  897. + if ((ctrl->ns_count == 0) || nvme_create_io_queues(ctrl)) {
  898. + /* No point to continue, if the controller says it doesn't have
  899. + namespaces or we couldn't create I/O queues. */
  900. + goto err_destroy_admin_sq;
  901. + }
  902. +
  903. + /* Give the controller a global number */
  904. + ctrl->ctrlnum = grub_nvme_ctrlcnt++;
  905. +
  906. + /* Populate namespace IDs */
  907. + for (grub_uint32_t ns_idx = 0; ns_idx < ctrl->ns_count; ns_idx++) {
  908. + nvme_probe_ns(ctrl, ns_idx, mdts);
  909. + }
  910. +
  911. + grub_dprintf("nvme", "NVMe initialization complete!\n");
  912. + return 0;
  913. +
  914. + err_destroy_admin_sq:
  915. + nvme_destroy_sq(&ctrl->admin_sq);
  916. + err_destroy_admin_cq:
  917. + nvme_destroy_cq(&ctrl->admin_cq);
  918. + return -1;
  919. +}
  920. +
  921. +static int grub_nvme_pci_probe(grub_pci_device_t dev, grub_pci_id_t pciid __attribute__ ((unused)), void *data __attribute__ ((unused)))
  922. +{
  923. + grub_pci_address_t addr;
  924. + grub_uint32_t class, bar, version;
  925. + struct nvme_reg volatile *reg;
  926. +
  927. + class = grub_pci_read (grub_pci_make_address (dev, GRUB_PCI_REG_CLASS));
  928. + if (class >> 16 != 0x0108)
  929. + return 0;
  930. + if ((class >> 8 & 0xff) != 2) { /* as of NVM 1.0e */
  931. + grub_dprintf("nvme", "Found incompatble NVMe: prog-if=%02x\n", class >> 8 & 0xff);
  932. + return 0;
  933. + }
  934. +
  935. + bar = grub_pci_read (grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0));
  936. + reg = grub_pci_device_map_range (dev, bar & GRUB_PCI_ADDR_MEM_MASK, sizeof (*reg));
  937. +
  938. + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
  939. + grub_pci_write_word (addr, grub_pci_read_word (addr) | GRUB_PCI_COMMAND_MEM_ENABLED);
  940. +
  941. + version = reg->vs;
  942. + grub_dprintf("nvme", "Found NVMe controller with version %u.%u.%u.\n", version >> 16, (version >> 8) & 0xFF, version & 0xFF);
  943. + grub_dprintf("nvme", " Capabilities %016llx\n", reg->cap);
  944. +
  945. + if (~reg->cap & NVME_CAP_CSS_NVME) {
  946. + grub_dprintf("nvme", "Controller doesn't speak NVMe command set. Skipping.\n");
  947. + goto err;
  948. + }
  949. +
  950. + struct nvme_ctrl *ctrl = grub_malloc(sizeof(*ctrl));
  951. + if (!ctrl)
  952. + goto err;
  953. +
  954. + grub_memset(ctrl, 0, sizeof(*ctrl));
  955. +
  956. + ctrl->reg = reg;
  957. + ctrl->pci = dev;
  958. +
  959. + if (grub_nvme_controller_enable(ctrl))
  960. + goto err_free_ctrl;
  961. +
  962. + return 0;
  963. +
  964. + err_free_ctrl:
  965. + grub_free(ctrl);
  966. + err:
  967. + grub_dprintf("nvme", "Failed to enable NVMe controller.\n");
  968. + return 0;
  969. +}
  970. +
  971. +static int
  972. +grub_nvme_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, grub_disk_pull_t pull)
  973. +{
  974. + struct nvme_namespace *ns;
  975. +
  976. + if (pull != GRUB_DISK_PULL_NONE)
  977. + return 0;
  978. +
  979. + FOR_LIST_ELEMENTS(ns, grub_nvme_namespaces)
  980. + if (hook (ns->devname, hook_data))
  981. + return 1;
  982. +
  983. + return 0;
  984. +}
  985. +
  986. +static grub_err_t
  987. +grub_nvme_open (const char *name __attribute ((unused)), grub_disk_t disk __attribute ((unused)))
  988. +{
  989. + struct nvme_namespace *ns;
  990. +
  991. + FOR_LIST_ELEMENTS(ns, grub_nvme_namespaces)
  992. + if (grub_strcmp (ns->devname, name) == 0)
  993. + break;
  994. +
  995. + if (! ns)
  996. + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
  997. +
  998. + disk->total_sectors = ns->lba_count;
  999. + disk->max_agglomerate = ns->max_req_size;
  1000. +
  1001. + disk->id = ns->nsnum; /* global id of the namespace */
  1002. +
  1003. + disk->data = ns;
  1004. +
  1005. + return 0;
  1006. +}
  1007. +
  1008. +static grub_err_t
  1009. +nvme_readwrite(struct nvme_namespace *ns, grub_disk_addr_t sector, grub_size_t num_sectors, char *buf, int write)
  1010. +{
  1011. + for (int i = 0; i < num_sectors;) {
  1012. + grub_uint16_t blocks_remaining = num_sectors - i;
  1013. + char *op_buf = buf + i * ns->block_size;
  1014. + int blocks = nvme_prpl_xfer(ns, sector + i, op_buf, blocks_remaining, write);
  1015. + if (blocks < 0)
  1016. + return GRUB_ERR_IO;
  1017. + i += blocks;
  1018. + }
  1019. + return GRUB_ERR_NONE;
  1020. +}
  1021. +
  1022. +static grub_err_t
  1023. +grub_nvme_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t num_sectors, char *buf)
  1024. +{
  1025. + return nvme_readwrite((struct nvme_namespace *) disk->data, sector, num_sectors, buf, 0);
  1026. +}
  1027. +
  1028. +static grub_err_t
  1029. +grub_nvme_write (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t num_sectors, const char *buf)
  1030. +{
  1031. + return nvme_readwrite((struct nvme_namespace *) disk->data, sector, num_sectors, buf, 1);
  1032. +}
  1033. +
  1034. +static struct grub_disk_dev grub_nvme_dev =
  1035. + {
  1036. + .name = "nvme",
  1037. + .id = GRUB_DISK_DEVICE_NVME_ID,
  1038. + .disk_iterate = grub_nvme_iterate,
  1039. + .disk_open = grub_nvme_open,
  1040. + .disk_read = grub_nvme_read,
  1041. + .disk_write = grub_nvme_write,
  1042. + .next = 0
  1043. + };
  1044. +
  1045. +GRUB_MOD_INIT(nvme)
  1046. +{
  1047. + grub_stop_disk_firmware ();
  1048. + grub_pci_iterate (grub_nvme_pci_probe, NULL);
  1049. + grub_disk_dev_register (&grub_nvme_dev);
  1050. +}
  1051. +
  1052. +GRUB_MOD_FINI(nvme)
  1053. +{
  1054. + grub_disk_dev_unregister (&grub_nvme_dev);
  1055. +}
  1056. diff --git a/include/grub/disk.h b/include/grub/disk.h
  1057. index fbf23df7f..186e76f0b 100644
  1058. --- a/include/grub/disk.h
  1059. +++ b/include/grub/disk.h
  1060. @@ -52,6 +52,7 @@ enum grub_disk_dev_id
  1061. GRUB_DISK_DEVICE_UBOOTDISK_ID,
  1062. GRUB_DISK_DEVICE_XEN,
  1063. GRUB_DISK_DEVICE_OBDISK_ID,
  1064. + GRUB_DISK_DEVICE_NVME_ID
  1065. };
  1066. struct grub_disk;
  1067. --
  1068. 2.39.2