]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
Add support for Replay Protection Memory Block (RPMB) commands
authorHanumanthu H <hanumanthuh@micron.com>
Tue, 4 Aug 2020 10:30:39 +0000 (16:00 +0530)
committerKeith Busch <kbusch@kernel.org>
Mon, 7 Dec 2020 15:09:35 +0000 (08:09 -0700)
Documentation/nvme-rpmb.1 [new file with mode: 0644]
Documentation/nvme-rpmb.html [new file with mode: 0644]
Documentation/nvme-rpmb.txt [new file with mode: 0644]
Makefile
nvme-builtin.h
nvme-print.c
nvme-rpmb.c [new file with mode: 0644]
nvme.c

diff --git a/Documentation/nvme-rpmb.1 b/Documentation/nvme-rpmb.1
new file mode 100644 (file)
index 0000000..7449243
--- /dev/null
@@ -0,0 +1,326 @@
+'\" t
+.\"     Title: nvme-rpmb
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
+.\"      Date: 08/04/2020
+.\"    Manual: NVMe Manual
+.\"    Source: NVMe
+.\"  Language: English
+.\"
+.TH "NVME\-RPMB" "1" "08/04/2020" "NVMe" "NVMe Manual"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+nvme-rpmb \- Send RPMB commands to an NVMe device
+.SH "SYNOPSIS"
+.sp
+.nf
+\fInvme rpmb\fR <device> [\-\-cmd=<command> | \-c <command>]
+                    [\-\-msgfile=<data\-file> | \-f <data\-file>]
+                    [\-\-keyfile=<key\-file> | \-g <key\-file>]
+                    [\-\-key=<key> | \-k <key>]
+                    [\-\-msg=<data> | \-d <data>]
+                    [\-\-address=<offset> | \-o <offset>]
+                    [\-\-blocks=<512 byte sectors> | \-b <sectors> ]
+                    [\-\-target=<target\-id> | \-t <id> ]
+.fi
+.SH "DESCRIPTION"
+.sp
+For the NVMe device given, send an nvme rpmb command and provide the results\&.
+.sp
+The <device> parameter is mandatory and NVMe character device (ex: /dev/nvme0) must be specified\&. If the given device supports RPMB targets, command given with \-\-cmd or \-c option shall be sent to the controller\&. If given NVMe device doesn\(cqt support RPMB targets, a message indicating the same shall be printed along with controller register values related RPMB\&.
+.SH "OPTIONS"
+.PP
+\-c <command>, \-\-cmd=<command>
+.RS 4
+RPMB command to be sent to the device\&. It can be one of the following
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+info          \- print information regarding supported RPMB targets and
+                access and total sizes\&. No further arguments are required
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+program\-key   \- program \*(Aqkey\*(Aq specified with \-k option or key read from
+                file specified with \-\-keyfile option to the specified
+                RPMB target given with \-\-target or \-t options\&. As per
+                spec, this is one time action which can\*(Aqt be undone\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+read\-couter   \- Read \*(Aqwrite counter\*(Aq of specified RPMB target\&. The
+                counter value read is printed onto STDOUT
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+read\-config   \- Read 512 bytes of device configuration block data of
+                specified RPMB target of the NVMe device\&. The data read
+                is written to input file specified with \-\-msgfile or \-f
+                option\&.
+write\-config  \- Write 512 byes of device configuration block data
+                from file specified by \-\-msgfile or \-f options to the
+                RPMB target specified with \-\-target or \-t options\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+read\-data     \- Supports authenticated data reading from specified
+                RPMB target (\-\-target or \-t option) at given offset
+                specified with \-\-address or \-o option, using key
+                specified using \-\-keyfile or \-k options\&. \-\-blocks or
+                \-o option should be given to read the amount of data
+                to be read in 512 byte blocks\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+write\-data    \- Supports authenticated data writting to specified RPMB
+                target (\-\-target or \-t option) at given offset
+                specified with \-\-address or \-o option, using key
+                specified using \-\-keyfile or \-k options\&. \-\-blocks or
+                \-o option should be given to indicate amount of data
+                to be written in 512 byte blocks\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+For data transfer (read/write) commands, if the specified size is not
+within the total size supported by a target, the request is failed
+nvme\-rpmb without sending it to device\&. RPMB target 0 is used as the
+default target if \-\-target or \-t is not specified\&. 0x0 is used as the
+default address if no \-address or \-o option is specified,
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.PP
+\-t <target>, \-\-target=<target>
+.RS 4
+RPMB target id\&. This should be one of the supported RPMB targets as reported by
+\fIinfo\fR
+command\&. If nothing is given, default of 0 is used as RPMB target\&.
+.RE
+.PP
+\-k <key>, \-\-key=<key>, \-g <key\-file>, \-\-keyfile=<key\-file>
+.RS 4
+Authentication key to be used for read/write commands\&. This should have been already programmed by
+\fIprogram\-key\fR
+command for given target\&. Key can be specified on command line using \-\-key or \-k options\&. Key can also be specified using file argument specified with \-\-keyfile or \-g options\&.
+.RE
+.PP
+\-f <data\-file>, \-\-msgfile=<data\-file>
+.RS 4
+Name of the file to be used for data transfer commands (read or write)\&. For read command, if an existing file is specified, it will be appended\&.
+.RE
+.PP
+\-d <data>, \-\-msg=<data>
+.RS 4
+These options provide the data on the command line itself\&.
+.RE
+.PP
+\-o <offset>, \-\-address=<offset>
+.RS 4
+The address (in 512 byte sector offset from 0) to be used for data trasnfer commands (read or write) for a specified RPMB target\&.
+.RE
+.PP
+\-b, \-\-blocks=<sectors>
+.RS 4
+The size in 512 byte sectors to be used for data trasnfer commands (read or write) for a specified RPMB target\&.
+.RE
+.SH "EXAMPLES"
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Print RPMB support information of an NVMe device
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# nvme rpmb /dev/nvme0 \-\-cmd=info
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Program
+\fISecreteKey\fR
+as authentication key for target 1
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# nvme rpmb /dev/nvme0 \-\-cmd=program\-key \-key=\*(AqSecretKey\*(Aq \-\-target=1
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Read current write counter of RPMB target 0
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# nvme rpmb /dev/nvme0 \-\-cmd=read\-counter \-\-target=0
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Read configuration data block of target 2 into config\&.bin file
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# nvme rpmb /dev/nvme0 \-\-cmd=read\-config \-\-target=2 \-f config\&.bin
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Write 200 blocks of (512 bytes) from input\&.bin onto target 0
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# nvme rpmb /dev/nvme0 \-c write\-data \-t 0 \-f input\&.bin \-b 200 \-k \*(AqSecreteKey\*(Aq
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+Read 200 blocks of (512 bytes) from target 2, at offset 0x100 and save the
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+data onto output\&.bin
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# nvme rpmb /dev/nvme0 \-c read\-data \-t 2 \-f out\&.bin \-b 200 \-o 0x100
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.SH "NVME"
+.sp
+Part of the nvme\-user suite
diff --git a/Documentation/nvme-rpmb.html b/Documentation/nvme-rpmb.html
new file mode 100644 (file)
index 0000000..d98ca07
--- /dev/null
@@ -0,0 +1,1008 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
+<head>\r
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
+<meta name="generator" content="AsciiDoc 8.6.10" />\r
+<title>nvme-rpmb(1)</title>\r
+<style type="text/css">\r
+/* Shared CSS for AsciiDoc xhtml11 and html5 backends */\r
+\r
+/* Default font. */\r
+body {\r
+  font-family: Georgia,serif;\r
+}\r
+\r
+/* Title font. */\r
+h1, h2, h3, h4, h5, h6,\r
+div.title, caption.title,\r
+thead, p.table.header,\r
+#toctitle,\r
+#author, #revnumber, #revdate, #revremark,\r
+#footer {\r
+  font-family: Arial,Helvetica,sans-serif;\r
+}\r
+\r
+body {\r
+  margin: 1em 5% 1em 5%;\r
+}\r
+\r
+a {\r
+  color: blue;\r
+  text-decoration: underline;\r
+}\r
+a:visited {\r
+  color: fuchsia;\r
+}\r
+\r
+em {\r
+  font-style: italic;\r
+  color: navy;\r
+}\r
+\r
+strong {\r
+  font-weight: bold;\r
+  color: #083194;\r
+}\r
+\r
+h1, h2, h3, h4, h5, h6 {\r
+  color: #527bbd;\r
+  margin-top: 1.2em;\r
+  margin-bottom: 0.5em;\r
+  line-height: 1.3;\r
+}\r
+\r
+h1, h2, h3 {\r
+  border-bottom: 2px solid silver;\r
+}\r
+h2 {\r
+  padding-top: 0.5em;\r
+}\r
+h3 {\r
+  float: left;\r
+}\r
+h3 + * {\r
+  clear: left;\r
+}\r
+h5 {\r
+  font-size: 1.0em;\r
+}\r
+\r
+div.sectionbody {\r
+  margin-left: 0;\r
+}\r
+\r
+hr {\r
+  border: 1px solid silver;\r
+}\r
+\r
+p {\r
+  margin-top: 0.5em;\r
+  margin-bottom: 0.5em;\r
+}\r
+\r
+ul, ol, li > p {\r
+  margin-top: 0;\r
+}\r
+ul > li     { color: #aaa; }\r
+ul > li > * { color: black; }\r
+\r
+.monospaced, code, pre {\r
+  font-family: "Courier New", Courier, monospace;\r
+  font-size: inherit;\r
+  color: navy;\r
+  padding: 0;\r
+  margin: 0;\r
+}\r
+pre {\r
+  white-space: pre-wrap;\r
+}\r
+\r
+#author {\r
+  color: #527bbd;\r
+  font-weight: bold;\r
+  font-size: 1.1em;\r
+}\r
+#email {\r
+}\r
+#revnumber, #revdate, #revremark {\r
+}\r
+\r
+#footer {\r
+  font-size: small;\r
+  border-top: 2px solid silver;\r
+  padding-top: 0.5em;\r
+  margin-top: 4.0em;\r
+}\r
+#footer-text {\r
+  float: left;\r
+  padding-bottom: 0.5em;\r
+}\r
+#footer-badges {\r
+  float: right;\r
+  padding-bottom: 0.5em;\r
+}\r
+\r
+#preamble {\r
+  margin-top: 1.5em;\r
+  margin-bottom: 1.5em;\r
+}\r
+div.imageblock, div.exampleblock, div.verseblock,\r
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,\r
+div.admonitionblock {\r
+  margin-top: 1.0em;\r
+  margin-bottom: 1.5em;\r
+}\r
+div.admonitionblock {\r
+  margin-top: 2.0em;\r
+  margin-bottom: 2.0em;\r
+  margin-right: 10%;\r
+  color: #606060;\r
+}\r
+\r
+div.content { /* Block element content. */\r
+  padding: 0;\r
+}\r
+\r
+/* Block element titles. */\r
+div.title, caption.title {\r
+  color: #527bbd;\r
+  font-weight: bold;\r
+  text-align: left;\r
+  margin-top: 1.0em;\r
+  margin-bottom: 0.5em;\r
+}\r
+div.title + * {\r
+  margin-top: 0;\r
+}\r
+\r
+td div.title:first-child {\r
+  margin-top: 0.0em;\r
+}\r
+div.content div.title:first-child {\r
+  margin-top: 0.0em;\r
+}\r
+div.content + div.title {\r
+  margin-top: 0.0em;\r
+}\r
+\r
+div.sidebarblock > div.content {\r
+  background: #ffffee;\r
+  border: 1px solid #dddddd;\r
+  border-left: 4px solid #f0f0f0;\r
+  padding: 0.5em;\r
+}\r
+\r
+div.listingblock > div.content {\r
+  border: 1px solid #dddddd;\r
+  border-left: 5px solid #f0f0f0;\r
+  background: #f8f8f8;\r
+  padding: 0.5em;\r
+}\r
+\r
+div.quoteblock, div.verseblock {\r
+  padding-left: 1.0em;\r
+  margin-left: 1.0em;\r
+  margin-right: 10%;\r
+  border-left: 5px solid #f0f0f0;\r
+  color: #888;\r
+}\r
+\r
+div.quoteblock > div.attribution {\r
+  padding-top: 0.5em;\r
+  text-align: right;\r
+}\r
+\r
+div.verseblock > pre.content {\r
+  font-family: inherit;\r
+  font-size: inherit;\r
+}\r
+div.verseblock > div.attribution {\r
+  padding-top: 0.75em;\r
+  text-align: left;\r
+}\r
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */\r
+div.verseblock + div.attribution {\r
+  text-align: left;\r
+}\r
+\r
+div.admonitionblock .icon {\r
+  vertical-align: top;\r
+  font-size: 1.1em;\r
+  font-weight: bold;\r
+  text-decoration: underline;\r
+  color: #527bbd;\r
+  padding-right: 0.5em;\r
+}\r
+div.admonitionblock td.content {\r
+  padding-left: 0.5em;\r
+  border-left: 3px solid #dddddd;\r
+}\r
+\r
+div.exampleblock > div.content {\r
+  border-left: 3px solid #dddddd;\r
+  padding-left: 0.5em;\r
+}\r
+\r
+div.imageblock div.content { padding-left: 0; }\r
+span.image img { border-style: none; vertical-align: text-bottom; }\r
+a.image:visited { color: white; }\r
+\r
+dl {\r
+  margin-top: 0.8em;\r
+  margin-bottom: 0.8em;\r
+}\r
+dt {\r
+  margin-top: 0.5em;\r
+  margin-bottom: 0;\r
+  font-style: normal;\r
+  color: navy;\r
+}\r
+dd > *:first-child {\r
+  margin-top: 0.1em;\r
+}\r
+\r
+ul, ol {\r
+    list-style-position: outside;\r
+}\r
+ol.arabic {\r
+  list-style-type: decimal;\r
+}\r
+ol.loweralpha {\r
+  list-style-type: lower-alpha;\r
+}\r
+ol.upperalpha {\r
+  list-style-type: upper-alpha;\r
+}\r
+ol.lowerroman {\r
+  list-style-type: lower-roman;\r
+}\r
+ol.upperroman {\r
+  list-style-type: upper-roman;\r
+}\r
+\r
+div.compact ul, div.compact ol,\r
+div.compact p, div.compact p,\r
+div.compact div, div.compact div {\r
+  margin-top: 0.1em;\r
+  margin-bottom: 0.1em;\r
+}\r
+\r
+tfoot {\r
+  font-weight: bold;\r
+}\r
+td > div.verse {\r
+  white-space: pre;\r
+}\r
+\r
+div.hdlist {\r
+  margin-top: 0.8em;\r
+  margin-bottom: 0.8em;\r
+}\r
+div.hdlist tr {\r
+  padding-bottom: 15px;\r
+}\r
+dt.hdlist1.strong, td.hdlist1.strong {\r
+  font-weight: bold;\r
+}\r
+td.hdlist1 {\r
+  vertical-align: top;\r
+  font-style: normal;\r
+  padding-right: 0.8em;\r
+  color: navy;\r
+}\r
+td.hdlist2 {\r
+  vertical-align: top;\r
+}\r
+div.hdlist.compact tr {\r
+  margin: 0;\r
+  padding-bottom: 0;\r
+}\r
+\r
+.comment {\r
+  background: yellow;\r
+}\r
+\r
+.footnote, .footnoteref {\r
+  font-size: 0.8em;\r
+}\r
+\r
+span.footnote, span.footnoteref {\r
+  vertical-align: super;\r
+}\r
+\r
+#footnotes {\r
+  margin: 20px 0 20px 0;\r
+  padding: 7px 0 0 0;\r
+}\r
+\r
+#footnotes div.footnote {\r
+  margin: 0 0 5px 0;\r
+}\r
+\r
+#footnotes hr {\r
+  border: none;\r
+  border-top: 1px solid silver;\r
+  height: 1px;\r
+  text-align: left;\r
+  margin-left: 0;\r
+  width: 20%;\r
+  min-width: 100px;\r
+}\r
+\r
+div.colist td {\r
+  padding-right: 0.5em;\r
+  padding-bottom: 0.3em;\r
+  vertical-align: top;\r
+}\r
+div.colist td img {\r
+  margin-top: 0.3em;\r
+}\r
+\r
+@media print {\r
+  #footer-badges { display: none; }\r
+}\r
+\r
+#toc {\r
+  margin-bottom: 2.5em;\r
+}\r
+\r
+#toctitle {\r
+  color: #527bbd;\r
+  font-size: 1.1em;\r
+  font-weight: bold;\r
+  margin-top: 1.0em;\r
+  margin-bottom: 0.1em;\r
+}\r
+\r
+div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {\r
+  margin-top: 0;\r
+  margin-bottom: 0;\r
+}\r
+div.toclevel2 {\r
+  margin-left: 2em;\r
+  font-size: 0.9em;\r
+}\r
+div.toclevel3 {\r
+  margin-left: 4em;\r
+  font-size: 0.9em;\r
+}\r
+div.toclevel4 {\r
+  margin-left: 6em;\r
+  font-size: 0.9em;\r
+}\r
+\r
+span.aqua { color: aqua; }\r
+span.black { color: black; }\r
+span.blue { color: blue; }\r
+span.fuchsia { color: fuchsia; }\r
+span.gray { color: gray; }\r
+span.green { color: green; }\r
+span.lime { color: lime; }\r
+span.maroon { color: maroon; }\r
+span.navy { color: navy; }\r
+span.olive { color: olive; }\r
+span.purple { color: purple; }\r
+span.red { color: red; }\r
+span.silver { color: silver; }\r
+span.teal { color: teal; }\r
+span.white { color: white; }\r
+span.yellow { color: yellow; }\r
+\r
+span.aqua-background { background: aqua; }\r
+span.black-background { background: black; }\r
+span.blue-background { background: blue; }\r
+span.fuchsia-background { background: fuchsia; }\r
+span.gray-background { background: gray; }\r
+span.green-background { background: green; }\r
+span.lime-background { background: lime; }\r
+span.maroon-background { background: maroon; }\r
+span.navy-background { background: navy; }\r
+span.olive-background { background: olive; }\r
+span.purple-background { background: purple; }\r
+span.red-background { background: red; }\r
+span.silver-background { background: silver; }\r
+span.teal-background { background: teal; }\r
+span.white-background { background: white; }\r
+span.yellow-background { background: yellow; }\r
+\r
+span.big { font-size: 2em; }\r
+span.small { font-size: 0.6em; }\r
+\r
+span.underline { text-decoration: underline; }\r
+span.overline { text-decoration: overline; }\r
+span.line-through { text-decoration: line-through; }\r
+\r
+div.unbreakable { page-break-inside: avoid; }\r
+\r
+\r
+/*\r
+ * xhtml11 specific\r
+ *\r
+ * */\r
+\r
+div.tableblock {\r
+  margin-top: 1.0em;\r
+  margin-bottom: 1.5em;\r
+}\r
+div.tableblock > table {\r
+  border: 3px solid #527bbd;\r
+}\r
+thead, p.table.header {\r
+  font-weight: bold;\r
+  color: #527bbd;\r
+}\r
+p.table {\r
+  margin-top: 0;\r
+}\r
+/* Because the table frame attribute is overriden by CSS in most browsers. */\r
+div.tableblock > table[frame="void"] {\r
+  border-style: none;\r
+}\r
+div.tableblock > table[frame="hsides"] {\r
+  border-left-style: none;\r
+  border-right-style: none;\r
+}\r
+div.tableblock > table[frame="vsides"] {\r
+  border-top-style: none;\r
+  border-bottom-style: none;\r
+}\r
+\r
+\r
+/*\r
+ * html5 specific\r
+ *\r
+ * */\r
+\r
+table.tableblock {\r
+  margin-top: 1.0em;\r
+  margin-bottom: 1.5em;\r
+}\r
+thead, p.tableblock.header {\r
+  font-weight: bold;\r
+  color: #527bbd;\r
+}\r
+p.tableblock {\r
+  margin-top: 0;\r
+}\r
+table.tableblock {\r
+  border-width: 3px;\r
+  border-spacing: 0px;\r
+  border-style: solid;\r
+  border-color: #527bbd;\r
+  border-collapse: collapse;\r
+}\r
+th.tableblock, td.tableblock {\r
+  border-width: 1px;\r
+  padding: 4px;\r
+  border-style: solid;\r
+  border-color: #527bbd;\r
+}\r
+\r
+table.tableblock.frame-topbot {\r
+  border-left-style: hidden;\r
+  border-right-style: hidden;\r
+}\r
+table.tableblock.frame-sides {\r
+  border-top-style: hidden;\r
+  border-bottom-style: hidden;\r
+}\r
+table.tableblock.frame-none {\r
+  border-style: hidden;\r
+}\r
+\r
+th.tableblock.halign-left, td.tableblock.halign-left {\r
+  text-align: left;\r
+}\r
+th.tableblock.halign-center, td.tableblock.halign-center {\r
+  text-align: center;\r
+}\r
+th.tableblock.halign-right, td.tableblock.halign-right {\r
+  text-align: right;\r
+}\r
+\r
+th.tableblock.valign-top, td.tableblock.valign-top {\r
+  vertical-align: top;\r
+}\r
+th.tableblock.valign-middle, td.tableblock.valign-middle {\r
+  vertical-align: middle;\r
+}\r
+th.tableblock.valign-bottom, td.tableblock.valign-bottom {\r
+  vertical-align: bottom;\r
+}\r
+\r
+\r
+/*\r
+ * manpage specific\r
+ *\r
+ * */\r
+\r
+body.manpage h1 {\r
+  padding-top: 0.5em;\r
+  padding-bottom: 0.5em;\r
+  border-top: 2px solid silver;\r
+  border-bottom: 2px solid silver;\r
+}\r
+body.manpage h2 {\r
+  border-style: none;\r
+}\r
+body.manpage div.sectionbody {\r
+  margin-left: 3em;\r
+}\r
+\r
+@media print {\r
+  body.manpage div#toc { display: none; }\r
+}\r
+\r
+\r
+</style>\r
+<script type="text/javascript">\r
+/*<![CDATA[*/\r
+var asciidoc = {  // Namespace.\r
+\r
+/////////////////////////////////////////////////////////////////////\r
+// Table Of Contents generator\r
+/////////////////////////////////////////////////////////////////////\r
+\r
+/* Author: Mihai Bazon, September 2002\r
+ * http://students.infoiasi.ro/~mishoo\r
+ *\r
+ * Table Of Content generator\r
+ * Version: 0.4\r
+ *\r
+ * Feel free to use this script under the terms of the GNU General Public\r
+ * License, as long as you do not remove or alter this notice.\r
+ */\r
+\r
+ /* modified by Troy D. Hanson, September 2006. License: GPL */\r
+ /* modified by Stuart Rackham, 2006, 2009. License: GPL */\r
+\r
+// toclevels = 1..4.\r
+toc: function (toclevels) {\r
+\r
+  function getText(el) {\r
+    var text = "";\r
+    for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
+      if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.\r
+        text += i.data;\r
+      else if (i.firstChild != null)\r
+        text += getText(i);\r
+    }\r
+    return text;\r
+  }\r
+\r
+  function TocEntry(el, text, toclevel) {\r
+    this.element = el;\r
+    this.text = text;\r
+    this.toclevel = toclevel;\r
+  }\r
+\r
+  function tocEntries(el, toclevels) {\r
+    var result = new Array;\r
+    var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');\r
+    // Function that scans the DOM tree for header elements (the DOM2\r
+    // nodeIterator API would be a better technique but not supported by all\r
+    // browsers).\r
+    var iterate = function (el) {\r
+      for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
+        if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
+          var mo = re.exec(i.tagName);\r
+          if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {\r
+            result[result.length] = new TocEntry(i, getText(i), mo[1]-1);\r
+          }\r
+          iterate(i);\r
+        }\r
+      }\r
+    }\r
+    iterate(el);\r
+    return result;\r
+  }\r
+\r
+  var toc = document.getElementById("toc");\r
+  if (!toc) {\r
+    return;\r
+  }\r
+\r
+  // Delete existing TOC entries in case we're reloading the TOC.\r
+  var tocEntriesToRemove = [];\r
+  var i;\r
+  for (i = 0; i < toc.childNodes.length; i++) {\r
+    var entry = toc.childNodes[i];\r
+    if (entry.nodeName.toLowerCase() == 'div'\r
+     && entry.getAttribute("class")\r
+     && entry.getAttribute("class").match(/^toclevel/))\r
+      tocEntriesToRemove.push(entry);\r
+  }\r
+  for (i = 0; i < tocEntriesToRemove.length; i++) {\r
+    toc.removeChild(tocEntriesToRemove[i]);\r
+  }\r
+\r
+  // Rebuild TOC entries.\r
+  var entries = tocEntries(document.getElementById("content"), toclevels);\r
+  for (var i = 0; i < entries.length; ++i) {\r
+    var entry = entries[i];\r
+    if (entry.element.id == "")\r
+      entry.element.id = "_toc_" + i;\r
+    var a = document.createElement("a");\r
+    a.href = "#" + entry.element.id;\r
+    a.appendChild(document.createTextNode(entry.text));\r
+    var div = document.createElement("div");\r
+    div.appendChild(a);\r
+    div.className = "toclevel" + entry.toclevel;\r
+    toc.appendChild(div);\r
+  }\r
+  if (entries.length == 0)\r
+    toc.parentNode.removeChild(toc);\r
+},\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////\r
+// Footnotes generator\r
+/////////////////////////////////////////////////////////////////////\r
+\r
+/* Based on footnote generation code from:\r
+ * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html\r
+ */\r
+\r
+footnotes: function () {\r
+  // Delete existing footnote entries in case we're reloading the footnodes.\r
+  var i;\r
+  var noteholder = document.getElementById("footnotes");\r
+  if (!noteholder) {\r
+    return;\r
+  }\r
+  var entriesToRemove = [];\r
+  for (i = 0; i < noteholder.childNodes.length; i++) {\r
+    var entry = noteholder.childNodes[i];\r
+    if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")\r
+      entriesToRemove.push(entry);\r
+  }\r
+  for (i = 0; i < entriesToRemove.length; i++) {\r
+    noteholder.removeChild(entriesToRemove[i]);\r
+  }\r
+\r
+  // Rebuild footnote entries.\r
+  var cont = document.getElementById("content");\r
+  var spans = cont.getElementsByTagName("span");\r
+  var refs = {};\r
+  var n = 0;\r
+  for (i=0; i<spans.length; i++) {\r
+    if (spans[i].className == "footnote") {\r
+      n++;\r
+      var note = spans[i].getAttribute("data-note");\r
+      if (!note) {\r
+        // Use [\s\S] in place of . so multi-line matches work.\r
+        // Because JavaScript has no s (dotall) regex flag.\r
+        note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];\r
+        spans[i].innerHTML =\r
+          "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +\r
+          "' title='View footnote' class='footnote'>" + n + "</a>]";\r
+        spans[i].setAttribute("data-note", note);\r
+      }\r
+      noteholder.innerHTML +=\r
+        "<div class='footnote' id='_footnote_" + n + "'>" +\r
+        "<a href='#_footnoteref_" + n + "' title='Return to text'>" +\r
+        n + "</a>. " + note + "</div>";\r
+      var id =spans[i].getAttribute("id");\r
+      if (id != null) refs["#"+id] = n;\r
+    }\r
+  }\r
+  if (n == 0)\r
+    noteholder.parentNode.removeChild(noteholder);\r
+  else {\r
+    // Process footnoterefs.\r
+    for (i=0; i<spans.length; i++) {\r
+      if (spans[i].className == "footnoteref") {\r
+        var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");\r
+        href = href.match(/#.*/)[0];  // Because IE return full URL.\r
+        n = refs[href];\r
+        spans[i].innerHTML =\r
+          "[<a href='#_footnote_" + n +\r
+          "' title='View footnote' class='footnote'>" + n + "</a>]";\r
+      }\r
+    }\r
+  }\r
+},\r
+\r
+install: function(toclevels) {\r
+  var timerId;\r
+\r
+  function reinstall() {\r
+    asciidoc.footnotes();\r
+    if (toclevels) {\r
+      asciidoc.toc(toclevels);\r
+    }\r
+  }\r
+\r
+  function reinstallAndRemoveTimer() {\r
+    clearInterval(timerId);\r
+    reinstall();\r
+  }\r
+\r
+  timerId = setInterval(reinstall, 500);\r
+  if (document.addEventListener)\r
+    document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);\r
+  else\r
+    window.onload = reinstallAndRemoveTimer;\r
+}\r
+\r
+}\r
+asciidoc.install();\r
+/*]]>*/\r
+</script>\r
+</head>\r
+<body class="manpage">\r
+<div id="header">\r
+<h1>\r
+nvme-rpmb(1) Manual Page\r
+</h1>\r
+<h2>NAME</h2>\r
+<div class="sectionbody">\r
+<p>nvme-rpmb -\r
+   Send RPMB commands to an NVMe device\r
+</p>\r
+</div>\r
+</div>\r
+<div id="content">\r
+<div class="sect1">\r
+<h2 id="_synopsis">SYNOPSIS</h2>\r
+<div class="sectionbody">\r
+<div class="verseblock">\r
+<pre class="content"><em>nvme rpmb</em> &lt;device&gt; [--cmd=&lt;command&gt; | -c &lt;command&gt;]\r
+                    [--msgfile=&lt;data-file&gt; | -f &lt;data-file&gt;]\r
+                    [--keyfile=&lt;key-file&gt; | -g &lt;key-file&gt;]\r
+                    [--key=&lt;key&gt; | -k &lt;key&gt;]\r
+                    [--msg=&lt;data&gt; | -d &lt;data&gt;]\r
+                    [--address=&lt;offset&gt; | -o &lt;offset&gt;]\r
+                    [--blocks=&lt;512 byte sectors&gt; | -b &lt;sectors&gt; ]\r
+                    [--target=&lt;target-id&gt; | -t &lt;id&gt; ]</pre>\r
+<div class="attribution">\r
+</div></div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_description">DESCRIPTION</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>For the NVMe device given, send an nvme rpmb command and provide the results.</p></div>\r
+<div class="paragraph"><p>The &lt;device&gt; parameter is mandatory and NVMe character device (ex: /dev/nvme0)\r
+must be specified. If the given device supports RPMB targets, command given\r
+with --cmd or -c option shall be sent to the controller. If given NVMe device\r
+doesn&#8217;t support RPMB targets, a message indicating the same shall be printed\r
+along with controller register values related RPMB.</p></div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_options">OPTIONS</h2>\r
+<div class="sectionbody">\r
+<div class="dlist"><dl>\r
+<dt class="hdlist1">\r
+-c &lt;command&gt;\r
+</dt>\r
+<dt class="hdlist1">\r
+--cmd=&lt;command&gt;\r
+</dt>\r
+<dd>\r
+<p>\r
+        RPMB command to be sent to the device. It can be one of the following\r
+</p>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>info          - print information regarding supported RPMB targets and\r
+                access and total sizes. No further arguments are required</code></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>program-key   - program 'key' specified with -k option or key read from\r
+                file specified with --keyfile option to the specified\r
+                RPMB target given with --target or -t options. As per\r
+                spec, this is one time action which can't be undone.</code></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>read-couter   - Read 'write counter' of specified RPMB target. The\r
+                counter value read is printed onto STDOUT</code></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>read-config   - Read 512 bytes of device configuration block data of\r
+                specified RPMB target of the NVMe device. The data read\r
+                is written to input file specified with --msgfile or -f\r
+                option.\r
+write-config  - Write 512 byes of device configuration block data\r
+                from file specified by --msgfile or -f options to the\r
+                RPMB target specified with --target or -t options.</code></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>read-data     - Supports authenticated data reading from specified\r
+                RPMB target (--target or -t option) at given offset\r
+                specified with --address or -o option, using key\r
+                specified using --keyfile or -k options. --blocks or\r
+                -o option should be given to read the amount of data\r
+                to be read in 512 byte blocks.</code></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>write-data    - Supports authenticated data writting to specified RPMB\r
+                target (--target or -t option) at given offset\r
+                specified with --address or -o option, using key\r
+                specified using --keyfile or -k options. --blocks or\r
+                -o option should be given to indicate amount of data\r
+                to be written in 512 byte blocks.</code></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>For data transfer (read/write) commands, if the specified size is not\r
+within the total size supported by a target, the request is failed\r
+nvme-rpmb without sending it to device. RPMB target 0 is used as the\r
+default target if --target or -t is not specified. 0x0 is used as the\r
+default address if no -address or -o option is specified,</code></pre>\r
+</div></div>\r
+</dd>\r
+<dt class="hdlist1">\r
+-t &lt;target&gt;\r
+</dt>\r
+<dt class="hdlist1">\r
+--target=&lt;target&gt;\r
+</dt>\r
+<dd>\r
+<p>\r
+        RPMB target id. This should be one of the supported RPMB targets as\r
+        reported by <em>info</em> command. If nothing is given, default of 0 is used\r
+        as RPMB target.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+-k &lt;key&gt;\r
+</dt>\r
+<dt class="hdlist1">\r
+--key=&lt;key&gt;\r
+</dt>\r
+<dt class="hdlist1">\r
+-g &lt;key-file&gt;\r
+</dt>\r
+<dt class="hdlist1">\r
+--keyfile=&lt;key-file&gt;\r
+</dt>\r
+<dd>\r
+<p>\r
+        Authentication key to be used for read/write commands. This should have\r
+        been already programmed by <em>program-key</em> command for given target. Key\r
+        can be specified on command line using --key or -k options. Key can\r
+        also be specified using file argument specified with --keyfile or -g\r
+        options.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+-f &lt;data-file&gt;\r
+</dt>\r
+<dt class="hdlist1">\r
+--msgfile=&lt;data-file&gt;\r
+</dt>\r
+<dd>\r
+<p>\r
+        Name of the file to be used for data transfer commands (read or write).\r
+        For read command, if an existing file is specified, it will be appended.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+-d &lt;data&gt;\r
+</dt>\r
+<dt class="hdlist1">\r
+--msg=&lt;data&gt;\r
+</dt>\r
+<dd>\r
+<p>\r
+        These options provide the data on the command line itself.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+-o &lt;offset&gt;\r
+</dt>\r
+<dt class="hdlist1">\r
+--address=&lt;offset&gt;\r
+</dt>\r
+<dd>\r
+<p>\r
+        The address (in 512 byte sector offset from 0) to be used for data\r
+        trasnfer commands (read or write) for a specified RPMB target.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+-b\r
+</dt>\r
+<dt class="hdlist1">\r
+--blocks=&lt;sectors&gt;\r
+</dt>\r
+<dd>\r
+<p>\r
+        The size in 512 byte sectors to be used for data trasnfer commands\r
+        (read or write) for a specified RPMB target.\r
+</p>\r
+</dd>\r
+</dl></div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_examples">EXAMPLES</h2>\r
+<div class="sectionbody">\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+Print RPMB support information of an NVMe device\r
+</p>\r
+<div class="listingblock">\r
+<div class="content">\r
+<pre><code># nvme rpmb /dev/nvme0 --cmd=info</code></pre>\r
+</div></div>\r
+</li>\r
+<li>\r
+<p>\r
+Program <em>SecreteKey</em> as authentication key for target 1\r
+</p>\r
+<div class="listingblock">\r
+<div class="content">\r
+<pre><code># nvme rpmb /dev/nvme0 --cmd=program-key -key='SecretKey' --target=1</code></pre>\r
+</div></div>\r
+</li>\r
+<li>\r
+<p>\r
+Read current write counter of RPMB target 0\r
+</p>\r
+<div class="listingblock">\r
+<div class="content">\r
+<pre><code># nvme rpmb /dev/nvme0 --cmd=read-counter --target=0</code></pre>\r
+</div></div>\r
+</li>\r
+<li>\r
+<p>\r
+Read configuration data block of target 2 into config.bin file\r
+</p>\r
+<div class="listingblock">\r
+<div class="content">\r
+<pre><code># nvme rpmb /dev/nvme0 --cmd=read-config --target=2 -f config.bin</code></pre>\r
+</div></div>\r
+</li>\r
+<li>\r
+<p>\r
+Write 200 blocks of (512 bytes) from input.bin onto target 0\r
+</p>\r
+<div class="listingblock">\r
+<div class="content">\r
+<pre><code># nvme rpmb /dev/nvme0 -c write-data -t 0 -f input.bin -b 200 -k 'SecreteKey'</code></pre>\r
+</div></div>\r
+</li>\r
+<li>\r
+<p>\r
+Read 200 blocks of (512 bytes) from target 2, at offset 0x100 and save the\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+data onto output.bin\r
+</p>\r
+<div class="listingblock">\r
+<div class="content">\r
+<pre><code># nvme rpmb /dev/nvme0 -c read-data -t 2 -f out.bin -b 200 -o 0x100</code></pre>\r
+</div></div>\r
+</li>\r
+</ul></div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_nvme">NVME</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>Part of the nvme-user suite</p></div>\r
+</div>\r
+</div>\r
+</div>\r
+<div id="footnotes"><hr /></div>\r
+<div id="footer">\r
+<div id="footer-text">\r
+Last updated\r
+ 2020-08-04 15:06:40 DST\r
+</div>\r
+</div>\r
+</body>\r
+</html>\r
diff --git a/Documentation/nvme-rpmb.txt b/Documentation/nvme-rpmb.txt
new file mode 100644 (file)
index 0000000..f9b371a
--- /dev/null
@@ -0,0 +1,150 @@
+nvme-rpmb(1)
+==============
+
+NAME
+----
+nvme-rpmb - Send RPMB commands to an NVMe device
+
+SYNOPSIS
+--------
+[verse]
+'nvme rpmb' <device> [--cmd=<command> | -c <command>]
+                   [--msgfile=<data-file> | -f <data-file>]
+                   [--keyfile=<key-file> | -g <key-file>]
+                   [--key=<key> | -k <key>]
+                   [--msg=<data> | -d <data>]
+                   [--address=<offset> | -o <offset>]
+                   [--blocks=<512 byte sectors> | -b <sectors> ]
+                   [--target=<target-id> | -t <id> ]
+
+DESCRIPTION
+-----------
+For the NVMe device given, send an nvme rpmb command and provide the results.
+
+The <device> parameter is mandatory and NVMe character device (ex: /dev/nvme0)
+must be specified. If the given device supports RPMB targets, command given
+with --cmd or -c option shall be sent to the controller. If given NVMe device
+doesn't support RPMB targets, a message indicating the same shall be printed
+along with controller register values related RPMB.
+
+OPTIONS
+-------
+-c <command>::
+--cmd=<command>::
+       RPMB command to be sent to the device. It can be one of the following
+
+       info          - print information regarding supported RPMB targets and
+                       access and total sizes. No further arguments are required
+
+       program-key   - program 'key' specified with -k option or key read from
+                       file specified with --keyfile option to the specified
+                       RPMB target given with --target or -t options. As per 
+                       spec, this is one time action which can't be undone.
+
+       read-couter   - Read 'write counter' of specified RPMB target. The
+                       counter value read is printed onto STDOUT
+
+       read-config   - Read 512 bytes of device configuration block data of
+                       specified RPMB target of the NVMe device. The data read
+                       is written to input file specified with --msgfile or -f
+                       option.
+       write-config  - Write 512 byes of device configuration block data
+                       from file specified by --msgfile or -f options to the
+                       RPMB target specified with --target or -t options.
+
+       read-data     - Supports authenticated data reading from specified
+                       RPMB target (--target or -t option) at given offset
+                       specified with --address or -o option, using key
+                       specified using --keyfile or -k options. --blocks or
+                       -o option should be given to read the amount of data
+                       to be read in 512 byte blocks.
+
+       write-data    - Supports authenticated data writting to specified RPMB
+                       target (--target or -t option) at given offset
+                       specified with --address or -o option, using key
+                       specified using --keyfile or -k options. --blocks or
+                       -o option should be given to indicate amount of data
+                       to be written in 512 byte blocks.
+
+       For data transfer (read/write) commands, if the specified size is not
+       within the total size supported by a target, the request is failed
+       nvme-rpmb without sending it to device. RPMB target 0 is used as the
+       default target if --target or -t is not specified. 0x0 is used as the
+       default address if no -address or -o option is specified, 
+       
+-t <target>::
+--target=<target>::
+       RPMB target id. This should be one of the supported RPMB targets as
+       reported by 'info' command. If nothing is given, default of 0 is used
+       as RPMB target.
+
+-k <key>::
+--key=<key>::
+-g <key-file>::
+--keyfile=<key-file>::
+       Authentication key to be used for read/write commands. This should have
+       been already programmed by 'program-key' command for given target. Key
+       can be specified on command line using --key or -k options. Key can
+       also be specified using file argument specified with --keyfile or -g 
+       options.
+
+-f <data-file>::
+--msgfile=<data-file>::
+       Name of the file to be used for data transfer commands (read or write).
+       For read command, if an existing file is specified, it will be appended.
+
+-d <data>::
+--msg=<data>::
+       These options provide the data on the command line itself. 
+-o <offset>::
+--address=<offset>::
+       The address (in 512 byte sector offset from 0) to be used for data 
+       trasnfer commands (read or write) for a specified RPMB target.
+-b::
+--blocks=<sectors>::
+       The size in 512 byte sectors to be used for data trasnfer commands
+       (read or write) for a specified RPMB target.
+
+EXAMPLES
+--------
+* Print RPMB support information of an NVMe device
++
+-----------
+# nvme rpmb /dev/nvme0 --cmd=info
+-----------
++
+* Program 'SecreteKey' as authentication key for target 1
++
+------------
+# nvme rpmb /dev/nvme0 --cmd=program-key -key='SecretKey' --target=1
+------------
++
+* Read current write counter of RPMB target 0
++
+------------
+# nvme rpmb /dev/nvme0 --cmd=read-counter --target=0
+------------
++
+* Read configuration data block of target 2 into config.bin file
++
+------------
+# nvme rpmb /dev/nvme0 --cmd=read-config --target=2 -f config.bin
+------------
++
+* Write 200 blocks of (512 bytes) from input.bin onto target 0
++
+------------
+# nvme rpmb /dev/nvme0 -c write-data -t 0 -f input.bin -b 200 -k 'SecreteKey'
+------------
++
+* Read 200 blocks of (512 bytes) from target 2, at offset 0x100 and save the
+* data onto output.bin
++
+------------
+# nvme rpmb /dev/nvme0 -c read-data -t 2 -f out.bin -b 200 -o 0x100
+------------
+
+NVME
+----
+Part of the nvme-user suite
+
index c6e1e27408261c3033ef10c272b3d2abea82b62b..c765821ad328e7c08bbe969c899f0f4e6e03ab47 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -58,7 +58,7 @@ override CFLAGS += -DNVME_VERSION='"$(NVME_VERSION)"'
 
 NVME_DPKG_VERSION=1~`lsb_release -sc`
 
-OBJS := nvme-print.o nvme-ioctl.o \
+OBJS := nvme-print.o nvme-ioctl.o nvme-rpmb.o \
        nvme-lightnvm.o fabrics.o nvme-models.o plugin.o \
        nvme-status.o nvme-filters.o nvme-topology.o
 
index 87338b5e5d5b271a0c956671299834d3943ae55f..21b6ff5e7c307808248df6891cbbe40f7e157815 100644 (file)
@@ -76,6 +76,7 @@ COMMAND_LIST(
        ENTRY("dir-receive", "Submit a Directive Receive command, return results", dir_receive)
        ENTRY("dir-send", "Submit a Directive Send command, return results", dir_send)
        ENTRY("virt-mgmt", "Manage Flexible Resources between Primary and Secondary Controller ", virtual_mgmt)
+       ENTRY("rpmb", "Replay Protection Memory Block commands", rpmb_cmd)
 );
 
 #endif
index 55299358aa9f49b02577fc7cf9521b7eff2bd438..88b97314e7270bbb791d606cd42cbd8ddedb2aa0 100644 (file)
@@ -1975,7 +1975,7 @@ static void nvme_show_id_ctrl_apsta(__u8 apsta)
        printf("\n");
 }
 
-static void nvme_show_id_ctrl_rpmbs(__le32 ctrl_rpmbs)
+void nvme_show_id_ctrl_rpmbs(__le32 ctrl_rpmbs)
 {
        __u32 rpmbs = le32_to_cpu(ctrl_rpmbs);
        __u32 asz = (rpmbs & 0xFF000000) >> 24;
diff --git a/nvme-rpmb.c b/nvme-rpmb.c
new file mode 100644 (file)
index 0000000..cfa9d2c
--- /dev/null
@@ -0,0 +1,1003 @@
+/*
+ * Copyright (C) 2020 Micron Techologies Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * nvme-rpmb.c - Implementation of NVMe RPMB support commands in Nvme
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <linux/if_alg.h>
+#include <linux/socket.h>
+
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+
+#define CREATE_CMD
+
+
+#ifndef AF_ALG
+#define AF_ALG 38
+#endif
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+#define HMAC_SHA256_ALGO_NAME          "hmac(sha256)"
+#define MD5_HASH_ALGO_NAME             "md5"
+#define HMAC_SHA256_HASH_SIZE          32
+#define MD5_HASH_HASH_SIZE             16
+
+extern int nvme_show_id_ctrl_rpmbs(unsigned int);
+/*
+ * Utility function to create hash value of given data (with given key) using
+ * given hash algorithm; this function uses kernel crypto services
+ */
+unsigned char *create_hash(const char *algo,
+                          int hash_size,
+                          unsigned char *data,
+                          int datalen,
+                          unsigned char *key,
+                          int keylen)
+{
+       int error, infd, outfd = -1;
+       unsigned char *hash = NULL;
+       struct sockaddr_alg provider_sa = {
+               .salg_family = AF_ALG,
+               .salg_type = "hash",
+               .salg_name = { 0 }
+       };
+
+       /* copy algorith name */
+       memcpy(provider_sa.salg_name, algo, strlen(algo));
+
+       /* open netlink socket connection to algorigm provider and bind */
+       infd = socket(AF_ALG, SOCK_SEQPACKET, 0);
+       if (infd < 0) {
+               perror("socket");
+               return hash;
+       }
+       error = bind(infd, (struct sockaddr *)&provider_sa, sizeof(provider_sa));
+       if (error < 0) {
+               perror("bind");
+               goto out;
+       }
+
+       /* if algorithm requires key, set it first - empty keys not accepted !*/
+       if (key != NULL && keylen > 0) {
+               error = setsockopt(infd, SOL_ALG, ALG_SET_KEY, key, keylen);
+               if (error < 0) {
+                       perror("setsockopt");
+                       goto out;
+               }
+       }
+               
+       /* now send data to hash */
+       outfd = accept(infd, NULL, 0);
+       if (outfd < 0) {
+               perror("accept");
+               goto out;
+       }
+       error = send(outfd, data, datalen, 0);
+       if (error < 0) {
+               perror("send");
+               goto out;
+       }
+
+       /* read computed hash */
+       hash = (unsigned char *)calloc(hash_size, 1);
+       if (hash == NULL) {
+               perror("calloc");
+               goto out;
+       }
+
+       error = read(outfd, hash, hash_size);
+       if (error != hash_size) {
+               perror("read");
+               free(hash);
+               hash = NULL;
+       }
+out:
+    if (outfd > 0) close(outfd);
+    if (infd > 0)  close(infd);
+
+    return hash;
+}
+
+/* Function that computes hmac-sha256 hash of given data and key pair. Returns
+ * byte stream (non-null terminated) upon success, NULL otherwise.
+ */
+unsigned char *
+hmac_sha256(unsigned char *data, int datalen, unsigned char *key, int keylen)
+{
+       return create_hash(HMAC_SHA256_ALGO_NAME,
+                          HMAC_SHA256_HASH_SIZE,
+                          data,
+                          datalen,
+                          key,
+                          keylen);
+}
+
+/* Function that computes md5 of given buffer - md5 hash is used as nonce
+ * Returns byte stream (non-null terminated) upon success, NULL otherwise.
+ */
+unsigned char *
+rpmb_md5(unsigned char *data, int datalen)
+{
+       return create_hash(MD5_HASH_ALGO_NAME,
+                          MD5_HASH_HASH_SIZE,
+                          data,
+                          datalen,
+                          NULL,
+                          0);
+}
+
+/* Read data from given file into buffer and return its length */
+static int read_file(const char *file, unsigned char **data, unsigned int *len)
+{
+       struct stat sb;
+       size_t size;
+       unsigned char   *buf = NULL;
+       int fd;
+       int err = -EINVAL;
+
+       if (file == NULL) return err;
+
+       if ((fd = open(file, O_RDONLY)) < 0) {
+               fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+               return fd;
+       }
+
+       err = fstat(fd, &sb);
+       if (err < 0) {
+               perror("fstat");
+               goto out;
+       }
+
+       size = sb.st_size;
+       if (posix_memalign((void **)&buf, getpagesize(), size)) {
+               fprintf(stderr, "No memory for reading file :%s\n", file);
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = read(fd, buf, size);
+       if (err < 0) {
+               err = -errno;
+               fprintf(stderr, "Failed to read data from file"
+                               " %s with %s\n", file, strerror(errno));
+               free(buf);
+               goto out;
+       }
+       err -= size;
+       *data = buf; 
+       *len = size;
+out:
+       close(fd);
+       return err;
+}
+
+/* Write given buffer data to specified file */
+static void write_file(unsigned char *data, size_t len, const char *dir,
+                         const char *file, const char *msg)
+{
+       char temp_folder[PATH_MAX] = { 0 };
+       FILE *fp = NULL;
+
+       if (dir != NULL)
+               sprintf(temp_folder, "%s/%s", dir, file);
+       else
+               sprintf(temp_folder, "./%s", file);
+
+       if ((fp = fopen(temp_folder, "ab+")) != NULL) {
+               if (fwrite(data, 1, len,  fp) != len) {
+                       fprintf(stderr, "Failed to write %s data to %s\n",
+                                msg, temp_folder);
+               }
+               fclose(fp);
+       } else  {
+               fprintf(stderr, "Failed to open %s file to write %s\n",
+                       temp_folder, msg);
+       }
+}
+
+/* Various definitions used in RPMB related support */
+enum rpmb_request_type {
+       RPMB_REQ_AUTH_KEY_PROGRAM = 0x0001,
+       RPMB_REQ_READ_WRITE_CNTR  = 0x0002,
+       RPMB_REQ_AUTH_DATA_WRITE  = 0x0003,
+       RPMB_REQ_AUTH_DATA_READ   = 0x0004,
+       RPMB_REQ_READ_RESULT      = 0x0005,
+       RPMB_REQ_AUTH_DCB_WRITE   = 0x0006,
+       RPMB_REQ_AUTH_DCB_READ    = 0x0007
+};
+       
+enum rpmb_response_type {
+       RPMB_RSP_AUTH_KEY_PROGRAM = (RPMB_REQ_AUTH_KEY_PROGRAM << 8),
+       RPMB_RSP_READ_WRITE_CNTR  = (RPMB_REQ_READ_WRITE_CNTR  << 8),
+       RPMB_RSP_AUTH_DATA_WRITE  = (RPMB_REQ_AUTH_DATA_WRITE  << 8),
+       RPMB_RSP_AUTH_DATA_READ   = (RPMB_REQ_AUTH_DATA_READ   << 8),
+       RPMB_RSP_READ_RESULT      = (RPMB_REQ_READ_RESULT      << 8),
+       RPMB_RSP_AUTH_DCB_WRITE   = (RPMB_REQ_AUTH_DCB_WRITE   << 8),
+       RPMB_RSP_AUTH_DCB_READ    = (RPMB_REQ_AUTH_DCB_READ    << 8)
+};
+
+/* RPMB data frame structure */
+#pragma pack(1)
+struct rpmb_data_frame_t {
+       unsigned char  pad[191];
+       unsigned char  mac[32];
+       unsigned char  target;     /* 0-6, should match with NSSF with SS, SR */
+       unsigned char  nonce[16];
+       unsigned int   write_counter;
+       unsigned int   address;
+       unsigned int   sectors;
+       unsigned short result;
+       unsigned short type;       /* req or response */
+       unsigned char  data[0];    /* in sector count times */
+};
+#pragma pack()
+       
+struct rpmb_config_block_t {
+       unsigned char  bp_enable;
+       unsigned char  bp_lock;
+       unsigned char  rsvd[510]; 
+};
+
+#define RPMB_DATA_FRAME_SIZE  256
+#define RPMB_NVME_SECP        0xEA 
+#define RPMB_NVME_SPSP        0x0001
+
+#define SEND_RPMB_REQ(tgt, size, req, result) \
+nvme_sec_send(fd, 0, tgt, RPMB_NVME_SPSP, RPMB_NVME_SECP, size, size, \
+               (unsigned char *)(req), (result))
+       
+#define RECV_RPMB_RSP(tgt, size, rsp, result) \
+nvme_sec_recv(fd, 0, tgt, RPMB_NVME_SPSP, RPMB_NVME_SECP, size, size, \
+               (unsigned char *)(rsp), (result))
+       
+/* Initialize nonce value in rpmb request frame */
+static void rpmb_nonce_init(struct rpmb_data_frame_t *req)
+{
+       int num = rand();
+       unsigned char *hash = rpmb_md5((unsigned char *)&num, sizeof(num));
+       if (hash) memcpy(req->nonce, hash, sizeof(req->nonce));
+}
+
+/* Read key from a given key buffer or key file */
+static unsigned char *read_rpmb_key(char *keystr, char *keyfile, unsigned int *keysize)
+{
+       unsigned char *keybuf = NULL;
+       
+       if (keystr == NULL) {
+               if (keyfile != NULL)
+                       read_file(keyfile, &keybuf, keysize);
+       } else if ((keybuf = (unsigned char *)malloc(strlen(keystr))) != NULL) {
+               *keysize = strlen(keystr);
+               memcpy(keybuf, keystr, *keysize);
+       }
+
+       return keybuf;
+}
+
+/* Initialize RPMB request frame with given values */
+static struct rpmb_data_frame_t *
+rpmb_request_init(unsigned int   req_size,
+                 unsigned short type,
+                 unsigned char  target,
+                 unsigned char  nonce,
+                 unsigned int   addr,
+                 unsigned int   sectors,
+                 unsigned char  *data,
+                 unsigned short data_offset,
+                 unsigned int   data_size)
+{
+       struct rpmb_data_frame_t *req = NULL;
+
+       if ((req = (struct rpmb_data_frame_t *)calloc(req_size, 1)) == NULL) {
+               fprintf(stderr, "Memory allocation failed for request 0x%04x\n",
+                       type);
+               return req;
+       }
+
+       req->type = type;
+       req->target = target;
+       req->address = addr;
+       req->sectors = sectors;
+       
+       if (nonce) rpmb_nonce_init(req);
+       if (data)  memcpy((unsigned char *)req + data_offset, data, data_size);
+
+       return req;
+}
+
+/* Process rpmb response and print appropriate error message */
+static int check_rpmb_response( struct rpmb_data_frame_t *req,
+                               struct rpmb_data_frame_t *rsp,
+                               char *msg)
+{
+       const char *rpmb_result_string [] = {
+               "Operation successful", 
+               "General failure",
+               "Authentication (MAC) failure",
+               "Counter failure (not matching/incrementing failure)",
+               "Address failure (out of range or wrong alignment)",
+               "Write (data/counter/result) failure",
+               "Read (data/counter/result) failure",
+               "Authentication key not yet programmed",
+               "Invalid device configuration block",
+               "Unknown error"
+       };
+        
+       /* check error status before comparing nonce and mac */
+       if (rsp->result != 0)  {
+               if (rsp->type != ((req->type << 8) & 0xFF00)) {
+                       fprintf(stderr, "%s ! non-matching response 0x%04x for"
+                               " 0x%04x\n", msg, rsp->type, req->type);
+               } else if ((rsp->result & 0x80) == 0x80) {
+                       fprintf(stderr, "%s ! Expired write-counter !\n", msg);
+               } else if (rsp->result) {
+                       fprintf(stderr, "%s ! %s\n", msg,
+                               rpmb_result_string[rsp->result & 0x7F]);
+               } else if (memcmp(req->nonce, rsp->nonce, 16)) {
+                       fprintf(stderr, "%s ! non-matching nonce\n", msg);
+               } else if (memcmp(req->mac, rsp->mac, 32)) {
+                       fprintf(stderr, "%s ! non-matching MAC\n", msg);
+               } else if ((req->write_counter + 1) != rsp->write_counter) {
+                       fprintf(stderr, "%s ! out-of-sync write-counters\n", msg);
+               }
+       }
+       
+       return (int)(rsp->result);
+}
+
+/* send an initialized rpmb request to the controller and read its response
+ * expected response size give in 'rsp_size'. returns response buffer upon
+ * successful completion (caller must free), NULL otherwise
+ */
+static struct rpmb_data_frame_t *
+rpmb_read_request(int fd, 
+                 struct rpmb_data_frame_t *req,
+                 int req_size,
+                 int rsp_size)
+{
+       struct rpmb_data_frame_t *rsp = NULL;
+       unsigned int result = 0;
+       unsigned char msg[1024] = { 0 };
+       int error;
+
+       sprintf((char *)msg, "RPMB request 0x%04x to target 0x%x",
+               req->type, req->target);
+
+       error = SEND_RPMB_REQ(req->target, req_size, req, &result);
+       if (error != 0) {
+               fprintf(stderr, "%s failed with error = 0x%x, result = %x\n",
+                       msg, error, result);
+               goto error_out;
+       }
+
+       /* read the result back */
+       rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1);
+       if (rsp == NULL) {
+               fprintf(stderr, "memory alloc failed for %s\n", msg);
+               goto error_out;
+       }
+
+       /* Read result of previous request */
+       error = RECV_RPMB_RSP(req->target, rsp_size, rsp, &result);
+       if (error) {
+               fprintf(stderr, "error 0x%x receiving response for %s\n",
+                       error, msg);
+               goto error_out;
+       }
+
+       /* validate response buffer - match target, nonce, and mac */
+       error = check_rpmb_response(req, rsp, (char *)msg);
+       if (error == 0) return rsp;
+
+error_out:
+       if (rsp) free(rsp);
+       return NULL;
+}
+
+/* read current write counter value from controller */
+static int rpmb_read_write_counter(int fd,
+                                  unsigned char target,
+                                  unsigned int *counter)
+{
+       int error = -1;
+       int req_size = sizeof(struct rpmb_data_frame_t);
+       struct rpmb_data_frame_t *req = NULL;
+       struct rpmb_data_frame_t *rsp = NULL;
+
+       req = rpmb_request_init(req_size, RPMB_REQ_READ_WRITE_CNTR,
+                               target, 1, 0, 0, NULL, 0, 0);
+       if (req == NULL) goto out;
+       if ((rsp = rpmb_read_request(fd, req, req_size, req_size)) == NULL) {
+               goto out;
+       }       
+       *counter = rsp->write_counter; 
+       error = 0;
+       
+out:
+       if (req) free(req);
+       if (rsp) free(rsp);
+       return error;
+}
+
+/* Read current device configuration block into specified buffer. It also returns
+ * current write counter value returned as part of response, in case of error it
+ * returns 0
+ */
+static unsigned int rpmb_read_config_block(int fd, unsigned char **config_buf)
+{
+       int req_size = sizeof(struct rpmb_data_frame_t);
+       int cfg_size = sizeof(struct rpmb_config_block_t);
+       int rsp_size = req_size + cfg_size;
+       
+       struct rpmb_data_frame_t   *req = NULL;
+       struct rpmb_data_frame_t   *rsp = NULL;
+       struct rpmb_config_block_t *cfg = NULL;
+       unsigned int retval = 0;
+
+       /* initialize request with nonce, no data on input */
+       req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DCB_READ, 0, 1, 0, 1,
+                               0, 0, 0);
+       if ((req == NULL) ||
+           (rsp = rpmb_read_request(fd, req, req_size, rsp_size)) == NULL)
+       {
+               goto out;
+       }       
+
+       /* copy configuration data to be sent back to caller */
+       cfg = (struct rpmb_config_block_t *)calloc(cfg_size, 1);
+       if (cfg == NULL) {
+               fprintf(stderr, "failed to allocate RPMB config buffer\n");
+               goto out;
+       }
+
+       memcpy(cfg, rsp->data, cfg_size);
+       *config_buf = (unsigned char *)cfg;
+       cfg = NULL;
+       retval = rsp->write_counter;
+out:
+       if (req) free(req);
+       if (rsp) free(rsp);
+       if (cfg) free(cfg);
+       return retval;
+}
+
+
+static int rpmb_auth_data_read(int fd, unsigned char target,
+                              unsigned int offset,
+                              unsigned char **msg_buf,
+                              int msg_size, int acc_size)
+{
+       struct rpmb_data_frame_t *req = NULL;
+       struct rpmb_data_frame_t *rsp = NULL;
+       int req_size = sizeof(struct rpmb_data_frame_t);
+       int chunk_size = (acc_size < msg_size) ? acc_size : msg_size;
+       int xfer = chunk_size;
+       unsigned char *bufp = (unsigned char *)malloc(msg_size * 512);
+       unsigned char *tbufp = bufp;
+       int data_size, rsp_size;
+       int error = -1;
+
+       if (bufp == NULL) {
+               fprintf(stderr, "Failed to allocated memory for read-data req\n");
+               goto out;
+       }
+       
+       while (xfer > 0) {
+               rsp_size = req_size + xfer * 512;
+               req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DATA_READ,
+                                       target, 1, offset, xfer, 0, 0, 0);
+               if (req == NULL) break;
+               if ((rsp = rpmb_read_request(fd, req, req_size, rsp_size)) == NULL)
+               {
+                       fprintf(stderr, "read_request failed\n");
+                       free(rsp);
+                       goto out;
+               }
+
+               data_size = rsp->sectors * 512;
+               memcpy(tbufp, rsp->data, data_size);
+               offset += rsp->sectors;
+               tbufp += data_size;
+               if (offset + chunk_size > msg_size)
+                       xfer = msg_size - offset;
+               else 
+                       xfer = chunk_size;
+               free(req);
+               free(rsp);
+       }
+       
+       *msg_buf = bufp;
+       error = offset;
+out:
+       return error;
+}
+
+/* Implementation of programming authentication key to given RPMB target */
+static int rpmb_program_auth_key(int fd, unsigned char target,
+                                unsigned char *key_buf, int key_size)
+{
+       int req_size = sizeof(struct rpmb_data_frame_t);
+       int rsp_size = sizeof(struct rpmb_data_frame_t);
+       
+       struct rpmb_data_frame_t *req = NULL;
+       struct rpmb_data_frame_t *rsp = NULL;
+       
+       int err = -ENOMEM;
+       unsigned int result = 0;
+       
+       req = rpmb_request_init(req_size, RPMB_REQ_AUTH_KEY_PROGRAM, target,
+                               0, 0, 0, key_buf, (223 - key_size), key_size);
+       if (req == NULL) {
+               fprintf(stderr, "failed to allocate request buffer memory\n");
+               goto out;
+       }
+
+       /* send request and read the result first */
+       rsp = rpmb_read_request(fd, req, req_size, rsp_size);
+       if (rsp == NULL || rsp->result != 0) {
+               goto out;
+       }
+
+       /* re-use response buffer */
+       memset(rsp, 0, rsp_size);
+       err = RECV_RPMB_RSP(req->target, rsp_size, (unsigned char *)rsp, &result);
+       if (err != 0 || result != 0) {
+               err = check_rpmb_response(req, rsp, "Failed to Program Key");
+       }
+out:
+       if (req) free(req);
+       if (rsp) free(rsp);
+       
+       return err;
+}
+
+
+/* Implementation of RPMB authenticated data write command; this function
+ * transfers msg_size bytes from msg_buf to controller 'addr'. Returns
+ * number of bytes actually written to, otherwise negetive error code
+ * on failures.
+ */
+static int auth_data_write_chunk(int fd, unsigned char tgt, unsigned int addr,
+                                unsigned char *msg_buf, int msg_size,
+                                unsigned char *keybuf, int keysize)
+{
+       int req_size = sizeof(struct rpmb_data_frame_t) + msg_size;
+       int rsp_size = sizeof(struct rpmb_data_frame_t);
+       
+       struct rpmb_data_frame_t *req = NULL;
+       struct rpmb_data_frame_t *rsp = NULL;
+       
+       unsigned int result = 0, write_cntr = 0;
+       unsigned char *mac = NULL;
+       int error  = -ENOMEM;
+
+       /* get current write counter and copy to the request  */
+       error = rpmb_read_write_counter(fd, tgt, &write_cntr);
+       if (error != 0) {
+          fprintf(stderr, "Failed to read write counter for write-data\n");
+           goto out;
+       }
+       
+       req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DATA_WRITE, tgt, 0,
+                               addr, (msg_size / 512), msg_buf,
+                               offsetof(struct rpmb_data_frame_t, data), msg_size);
+       if (req == NULL) {
+               fprintf(stderr, "Memory alloc failed for write-data command\n");
+               goto out;
+       }
+
+       req->write_counter = write_cntr;
+
+       /* compute HMAC hash */
+       mac = hmac_sha256(((unsigned char *)req + 223), req_size - 223,
+                          keybuf, keysize);
+       if (mac == NULL) {
+               fprintf(stderr, "failed to compute HMAC-SHA256\n");
+               error = -1;
+               goto out;
+       }
+
+       memcpy(req->mac, mac, 32);
+       
+       /* send the request and get response */
+       error = SEND_RPMB_REQ(tgt, req_size, (unsigned char *)req, &result);
+       if (error != 0) {
+           fprintf(stderr, "RPMB request 0x%04x for 0x%x, error: %d, result = %x\n",
+                   req->type, tgt, error, result);
+           goto out;
+       }
+       
+       /* send the request to get the result and then request to get the response */
+        rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1);
+       rsp->target = req->target;
+       rsp->type = RPMB_REQ_READ_RESULT;
+       error = SEND_RPMB_REQ(tgt, rsp_size, (unsigned char *)rsp, &result);
+       if (error != 0 || rsp->result != 0) {
+               fprintf(stderr, "Write-data read result 0x%x, error = 0x%x\n",
+                       rsp->result, error);
+               goto out;
+       }
+
+       /* Read final response */
+       memset(rsp, 0, rsp_size);
+       error = RECV_RPMB_RSP(tgt, rsp_size, (unsigned char *)rsp, &result);
+       if (error != 0)
+               fprintf(stderr, "Auth data write recv error = 0x%x\n", error);
+       else 
+               error = check_rpmb_response(req, rsp, "Failed to write-data");
+out:
+       if (req) free(req);
+       if (rsp) free(rsp);
+       if (mac) free(mac);
+
+       return error;
+}
+
+/* send the request and get response */
+static int rpmb_auth_data_write(int fd, unsigned char target,
+                               unsigned int addr, int acc_size,
+                               unsigned char *msg_buf, int msg_size,
+                               unsigned char *keybuf, int keysize)
+{
+       int chunk_size = acc_size < msg_size ? acc_size : msg_size;
+       int xfer   = chunk_size;
+       int offset = 0;
+
+       while (xfer > 0 ) {
+               if (auth_data_write_chunk(fd, target, (addr + offset / 512),
+                                         msg_buf + offset, xfer,
+                                         keybuf, keysize) != 0)
+               {
+                       /* error writing chunk data */
+                       break;  
+               }
+
+               offset += xfer;
+               if (offset + chunk_size > msg_size)
+                       xfer = msg_size - offset;
+               else 
+                       xfer = chunk_size;
+       }
+
+       return offset;
+}
+
+/* writes given config_block buffer to the drive target 0 */
+static int rpmb_write_config_block(int fd, unsigned char *cfg_buf,
+                                  unsigned char *keybuf, int keysize)
+{
+       int cfg_size = sizeof(struct rpmb_config_block_t);
+       int rsp_size = sizeof(struct rpmb_data_frame_t);
+       int req_size = rsp_size + cfg_size;
+       
+       struct rpmb_data_frame_t *req = NULL;
+       struct rpmb_data_frame_t *rsp = NULL;
+       unsigned char *cfg_buf_read = NULL, *mac = NULL;
+       unsigned int write_cntr = 0, result = 0;
+       int   error = -ENOMEM;
+       
+       /* initialize request */
+       req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DCB_WRITE, 0, 0, 0, 1,
+                               cfg_buf, offsetof(struct rpmb_data_frame_t, data),
+                               cfg_size);
+       if (req == NULL) {
+               fprintf(stderr, "failed to allocate rpmb request buffer\n");
+               goto out; 
+       }
+
+       /* read config block write_counter from controller */
+       write_cntr = rpmb_read_config_block(fd, &cfg_buf_read);
+       if (cfg_buf_read == NULL) {
+               fprintf(stderr, "failed to read config block write counter\n");
+               error = -EIO;
+               goto out;
+       }
+
+       free(cfg_buf_read);
+       req->write_counter = write_cntr;
+       mac = hmac_sha256(((unsigned char *)req + 223), req_size - 223,
+                          keybuf, keysize);
+       if (mac == NULL) {
+               fprintf(stderr, "failed to compute hmac-sha256 hash\n");
+               error = -EINVAL;
+               goto out;
+       }
+       
+       memcpy(req->mac, mac, sizeof(req->mac)); 
+       
+       error = SEND_RPMB_REQ(0, req_size, (unsigned char *)req, &result);
+       if (error != 0) {
+               fprintf(stderr, "Write-config RPMB request, error = 0x%x\n",
+                       error);
+               goto out;
+       }
+       
+       /* get response */
+       rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1);
+       if (rsp == NULL) {
+               fprintf(stderr, "failed to allocate response buffer memory\n");
+               error = -ENOMEM;
+               goto out;
+       }
+
+       /* get result first */
+       memset(rsp, 0, rsp_size);
+       rsp->target = req->target;
+       rsp->type = RPMB_REQ_READ_RESULT;
+       /* get the response and validate */
+       error = RECV_RPMB_RSP(req->target, rsp_size, rsp, &result);
+       if (error != 0) {
+               fprintf(stderr,"Failed getting write-config response\
+                       error = 0x%x\n", error);
+               goto out;
+       }
+       error = check_rpmb_response(req, rsp,
+                                 "Failed to retrieve write-config response");
+out:
+       if (req) free(req);
+       if (rsp) free(rsp);
+       if (mac) free(mac);
+       
+       return error;
+}
+
+static bool invalid_xfer_size(int blocks, unsigned int bpsz)
+{
+       return ((blocks <= 0) || 
+               (blocks * 512) > ((bpsz + 1) * 128 * 1024));
+}
+
+/* Handling rpmb sub-command */
+int rpmb_cmd_option(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+       const char *desc    = "Run RPMB command on the supporting controller";
+       const char *msg     = "data to be written on write-data or write-config commands";
+       const char *mfile   = "data file for read/write-data, read/write-config options";
+       const char *kfile   = "key file that has authentication key to be used";
+       const char *target  = "RPMB target - numerical value of 0 to 6, default 0";
+       const char *address = "Sector offset to read from or write to for an RPMB target, default 0";
+       const char *blocks  = "Number of 512 blocks to read or write";
+       const char *key     = "key to be used for authentication";
+       const char *opt     = "RPMB action - info, program-key, read-counter, write-data, " \
+                             "read-data, write-config and read-config";
+       
+       struct config {
+               char *cmd;
+               char *key;
+               char *msg;
+               char *keyfile;
+               char *msgfile;
+               int  opt;
+               int  address;
+               int  blocks; 
+               char target;
+       };
+       
+       struct config cfg = {
+               .cmd     = "info",
+               .key     = NULL,
+               .msg     = NULL,
+               .msgfile = NULL,
+               .keyfile = NULL,
+               .opt     = 0,
+               .address = 0,
+               .blocks  = 0,
+               .target  = 0,
+       };
+       
+       OPT_ARGS(opts) = {
+               OPT_STRING("cmd",     'c', "command", &cfg.cmd,     opt),
+               OPT_STRING("msgfile", 'f', "FILE",    &cfg.msgfile, mfile),
+               OPT_STRING("keyfile", 'g', "FILE",    &cfg.keyfile, kfile),
+               OPT_STRING("key",     'k', "key",     &cfg.key,     key),
+               OPT_STRING("msg",     'd', "data",    &cfg.msg,     msg),
+               OPT_UINT("address",   'o', &cfg.address,  address),
+               OPT_UINT("blocks",    'b', &cfg.blocks,   blocks),
+               OPT_UINT("target",    't', &cfg.target,   target),
+               OPT_END()
+       };
+       
+       unsigned int write_cntr = 0;
+       unsigned char *key_buf = NULL;
+       unsigned char *msg_buf = NULL;
+       unsigned int msg_size = 0;
+       unsigned int key_size = 0;
+       int fd = -1, err = -1;
+       struct nvme_id_ctrl ctrl;
+
+       union ctrl_rpmbs_reg {
+               struct {
+                       unsigned int num_targets:3;
+                       unsigned int auth_method:3;
+                       unsigned int reserved:10;
+                       unsigned int total_size:8;   /* 128K units */
+                       unsigned int access_size:8;  /* in 512 byte count */
+               };
+               unsigned int rpmbs;
+       } regs;
+       
+       if ((fd = parse_and_open(argc, argv, desc, opts)) < 0)
+               goto out;
+       
+       /* before parsing  commands, check if controller supports any RPMB targets */
+       err = nvme_identify_ctrl(fd, &ctrl);
+       if (err)
+               goto out;
+       
+       regs.rpmbs = le32_to_cpu(ctrl.rpmbs);
+       if (regs.num_targets == 0) {
+               fprintf(stderr, "No RPMB targets are supported by the drive\n");
+               goto out;
+       }
+       
+       /* parse and validate options; default print rpmb support info */
+       if (cfg.cmd == 0 || strcmp(cfg.cmd, "info") == 0) {
+               nvme_show_id_ctrl_rpmbs(regs.rpmbs);
+               goto out;
+       }
+       
+       if (strcmp(cfg.cmd, "program-key") == 0)
+               cfg.opt = RPMB_REQ_AUTH_KEY_PROGRAM;
+       else if (strcmp(cfg.cmd, "read-counter") == 0)
+               cfg.opt = RPMB_REQ_READ_WRITE_CNTR;
+       else if (strcmp(cfg.cmd, "write-data") == 0)
+               cfg.opt = RPMB_REQ_AUTH_DATA_WRITE;
+       else if (strcmp(cfg.cmd, "read-data") == 0)
+               cfg.opt = RPMB_REQ_AUTH_DATA_READ;
+       else if (strcmp(cfg.cmd, "write-config") == 0)
+               cfg.opt = RPMB_REQ_AUTH_DCB_WRITE;
+       else if (strcmp(cfg.cmd, "read-config") == 0)
+               cfg.opt = RPMB_REQ_AUTH_DCB_READ;
+       else {
+               fprintf(stderr, "Invalid option %s for rpmb command\n", cfg.cmd);
+               goto out;
+       }
+       
+       /* input file/data processing */
+       if (cfg.opt == RPMB_REQ_AUTH_DCB_WRITE || 
+           cfg.opt == RPMB_REQ_AUTH_DATA_WRITE ||
+           cfg.opt == RPMB_REQ_AUTH_KEY_PROGRAM)
+       {
+               key_buf = read_rpmb_key(cfg.key, cfg.keyfile, &key_size);
+               if (key_buf == NULL) {
+                       fprintf(stderr, "Failed to read key\n");
+                       goto out;
+               }
+       
+               if (key_size > 223 || key_size <= 0) {
+                       fprintf(stderr, "Invalid key size %d, valid input 1 to 223\n",
+                       key_size);
+                       goto out;
+               }
+
+               if (cfg.opt == RPMB_REQ_AUTH_DCB_WRITE ||
+                   cfg.opt == RPMB_REQ_AUTH_DATA_WRITE) {
+                       if (cfg.msg != NULL) {
+                               msg_size = strlen(cfg.msg);
+                               msg_buf = (unsigned char *)malloc(msg_size);
+                               memcpy(msg_buf, cfg.msg, msg_size);
+                       } else {
+                               err = read_file(cfg.msgfile, &msg_buf, &msg_size);
+                               if (err || msg_size <= 0) {
+                                       fprintf(stderr, "Failed to read file %s\n",
+                                               cfg.msgfile);
+                                       goto out;
+                               }
+                       }
+               }
+       }
+       
+       switch (cfg.opt) {
+               case RPMB_REQ_READ_WRITE_CNTR:
+                       err = rpmb_read_write_counter(fd, cfg.target, &write_cntr);
+                       if (err == 0)
+                               printf("Write Counter is: %u\n", write_cntr);
+                       break;
+       
+               case RPMB_REQ_AUTH_DCB_READ:
+                       write_cntr = rpmb_read_config_block(fd, &msg_buf);
+                       if (msg_buf == NULL) {
+                               fprintf(stderr, "failed read config blk\n");
+                               goto out;
+                       }
+
+                       /* no output file is given, print the data on stdout */
+                       if (cfg.msgfile == 0) {
+                               struct rpmb_config_block_t *cfg =
+                                               (struct rpmb_config_block_t *)msg_buf;
+                               printf("Boot Parition Protection is %s\n",
+                                       ((cfg->bp_enable & 0x1)  ? "Enabled" : "Disabled"));
+                               printf("Boot Parition 1 is %s\n",
+                                       ((cfg->bp_lock & 0x2) ? "Locked" : "Unlocked"));
+                               printf("Boot Parition 0 is %s\n",
+                                       ((cfg->bp_lock & 0x1) ? "Locked" : "Unlocked"));
+                       } else {
+                               printf("Saving received config data to %s file\n", cfg.msgfile);
+                               write_file(msg_buf, sizeof(struct rpmb_config_block_t), NULL,
+                                          cfg.msgfile, NULL);
+                       }
+                       err = (write_cntr == 0);
+                       break;
+       
+               case RPMB_REQ_AUTH_DATA_READ:
+                       /* check if requested data is beyond what target supports */
+                       msg_size = cfg.blocks * 512;
+                       if (invalid_xfer_size(cfg.blocks, regs.total_size)) {
+                               fprintf(stderr, "invalid transfer size %d \n",
+                                       msg_size);
+                               break;
+                       }
+                       err = rpmb_auth_data_read(fd, cfg.target, cfg.address,
+                                                 &msg_buf, cfg.blocks,
+                                                 (regs.access_size + 1));
+                       if (err > 0 && msg_buf != NULL) {
+                               printf("Writting %d bytes to file %s\n",
+                                       err * 512, cfg.msgfile);
+                               write_file(msg_buf, err * 512, NULL,
+                                          cfg.msgfile, NULL);
+                       }
+                       break;
+       
+               case RPMB_REQ_AUTH_DATA_WRITE:
+                       if (invalid_xfer_size(cfg.blocks, regs.total_size) || 
+                           (cfg.blocks * 512) > msg_size) {
+                               fprintf(stderr, "invalid transfer size %d\n", 
+                                       cfg.blocks * 512);
+                               break;
+                       } else if ((cfg.blocks * 512) < msg_size) {
+                               msg_size = cfg.blocks * 512;
+                       }
+                       err = rpmb_auth_data_write(fd, cfg.target, cfg.address,
+                                                 ((regs.access_size + 1) * 512),
+                                                  msg_buf, msg_size,
+                                                  key_buf, key_size);
+
+                       /* print whatever extent of data written to target */
+                       printf("Written %d sectors out of %d @target(%d):0x%x\n",
+                               err/512, msg_size/512, cfg.target, cfg.address);
+                       break;
+
+               case RPMB_REQ_AUTH_DCB_WRITE:
+                       err = rpmb_write_config_block(fd, msg_buf, key_buf, key_size);
+                       break;
+       
+               case RPMB_REQ_AUTH_KEY_PROGRAM:
+                       err = rpmb_program_auth_key(fd, cfg.target, key_buf, key_size);
+                       break;
+               default:
+                       break;
+       }
+        
+out:
+       /* release memory  */
+       if (key_buf) free(key_buf);
+       if (msg_buf) free(msg_buf);
+       
+       /* close file descriptor */
+       if (fd > 0) close(fd);
+       
+       return err;
+}
diff --git a/nvme.c b/nvme.c
index 9332372801f90c96ffdd1d26913c56b1e8917515..ee4b6cf56becca4f863a83c3e4c3da324d8cc34e 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -4732,6 +4732,13 @@ ret:
        return nvme_status_to_errno(err, false);
 }
 
+/* rpmb_cmd_option is defined in nvme-rpmb.c */
+extern int rpmb_cmd_option(int, char **, struct command *, struct plugin *);
+static int rpmb_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+       return rpmb_cmd_option(argc, argv, cmd, plugin);
+}
+
 static int passthru(int argc, char **argv, int ioctl_cmd, const char *desc, struct command *cmd)
 {
        void *data = NULL, *metadata = NULL;