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

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