gnuboot-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 12/20] import util/nvmutil from lbmk 09bed9a4


From: Leah Rowe
Subject: [PATCH 12/20] import util/nvmutil from lbmk 09bed9a4
Date: Sun, 14 Jan 2024 15:55:29 +0000

From: Leah Rowe <leah@libreboot.org>

this can be used to manipulate intel gbe regions, specifically
set checksums, swap parts, copy and set the mac address. it is
an alternative to ich9gen, when all you want to do is change
the mac address. you use it alongside ifdtool, to extract the
gbe file, operate on it using nvmutil and then re-insert with
ifdtool, on the target rom image. use this for gm45 machines
supported by gnuboot.

Signed-off-by: Leah Rowe <leah@libreboot.org>
---
 util/nvmutil/AUTHORS      |   1 +
 util/nvmutil/COPYING      |  20 ++
 util/nvmutil/ChangeLog.md |   8 +
 util/nvmutil/Makefile     |  16 ++
 util/nvmutil/README.md    | 506 ++++++++++++++++++++++++++++++++++++++
 util/nvmutil/nvm          | Bin 0 -> 17944 bytes
 util/nvmutil/nvmutil.c    | 281 +++++++++++++++++++++
 7 files changed, 832 insertions(+)
 create mode 100644 util/nvmutil/AUTHORS
 create mode 100644 util/nvmutil/COPYING
 create mode 100644 util/nvmutil/ChangeLog.md
 create mode 100644 util/nvmutil/Makefile
 create mode 100644 util/nvmutil/README.md
 create mode 100755 util/nvmutil/nvm
 create mode 100644 util/nvmutil/nvmutil.c

diff --git a/util/nvmutil/AUTHORS b/util/nvmutil/AUTHORS
new file mode 100644
index 0000000..f3c0038
--- /dev/null
+++ b/util/nvmutil/AUTHORS
@@ -0,0 +1 @@
+Leah Rowe
diff --git a/util/nvmutil/COPYING b/util/nvmutil/COPYING
new file mode 100644
index 0000000..784581d
--- /dev/null
+++ b/util/nvmutil/COPYING
@@ -0,0 +1,20 @@
+Copyright (C) 2022, 2023 Leah Rowe <leah@libreboot.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/util/nvmutil/ChangeLog.md b/util/nvmutil/ChangeLog.md
new file mode 100644
index 0000000..e1ed575
--- /dev/null
+++ b/util/nvmutil/ChangeLog.md
@@ -0,0 +1,8 @@
+This change log has moved. Please refer here for historical pre-osboot-merge
+changes:
+
+<https://libreboot.org/docs/install/nvmutilimport.html>
+
+Osboot merged with Libreboot on November 17th, 2022. For nvmutil changes after
+this date, please check regular Libreboot release announcements which shall
+now specify any such changes.
diff --git a/util/nvmutil/Makefile b/util/nvmutil/Makefile
new file mode 100644
index 0000000..f25f6dd
--- /dev/null
+++ b/util/nvmutil/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2022 Leah Rowe <leah@libreboot.org>
+# SPDX-FileCopyrightText: 2023 Riku Viitanen <riku.viitanen@protonmail.com>
+
+CC=cc
+CFLAGS=-Os -Wall -Wextra -Werror -pedantic
+PREFIX?=/usr/bin
+
+nvm: nvmutil.c
+       $(CC) $(CFLAGS) nvmutil.c -o nvm
+
+install: nvm
+       install nvm $(PREFIX)/nvm
+
+clean:
+       rm -f nvm
diff --git a/util/nvmutil/README.md b/util/nvmutil/README.md
new file mode 100644
index 0000000..5a7d4f5
--- /dev/null
+++ b/util/nvmutil/README.md
@@ -0,0 +1,506 @@
+---
+title: nvmutil manual
+x-toc-enable: true
+...
+
+With this software, you can change the MAC address inside GbE regions
+on any system that uses an Intel Flash Descriptor. This was imported
+from the Libreboot project.
+
+Introduction
+============
+
+This is the manual for `nvmutil`, including the GNU Boot
+build system under `util/nvmutil/`. This program lets you modify
+the MAC address, correct/verify/invalidate checksums,
+swap/copy and dump regions on Intel PHY NVM images,
+which are small binary configuration files that go
+in flash, for Gigabit (ethernet) Intel NICs.
+
+This software is largely targeted at coreboot users,
+but it can be used on most modern Intel systems, or
+most systems from about 2008/2009 onwards.
+
+NOTE: X200/X200T/X200S/T400/T400S/T500/W500/R400
+users should know that this software does *not* necessarily
+replace `ich9gen`, because that program generates entire
+ICH9M IFD+GbE regions, in addition to letting you set the
+MAC address. *This* program, `nvmutil`, can *also* set
+the MAC address on those machines, but it operates on a
+single GbE dump that is already created, and doesn't
+create one from scratch. You can use this as an alternative
+to ich9gen on ICH9M machines, if all you want to do is
+change the MAC address.
+
+This program is operated on dumps of the GbE NVM image,
+which normally goes in the boot flash (alongside BIOS/UEFI
+or coreboot, IFD and other regions in the flash). The first
+half of this README is dedicated to precisely this, telling
+you how to dump or otherwise acquire that file; the second
+half of this README then tells you how to operate on it,
+using `nvmutil`.
+
+How to download newer versions
+==============================
+
+Simply pull down the latest changes in GNU Boot. The `nvmutil`
+software is now part of GNU Boot, inherited from Libreboot.
+
+More info about git:
+
+* <https://git-scm.com/>
+
+Context
+=======
+
+On many Intel systems with an IFD (Intel Flash Descriptor), the
+Intel PHY (Gigabit Ethernet) stores its configuration, binary
+encoded, into a special region of the main boot flash, alongside
+other flash regions such as: IFD, ME, BIOS.
+
+This includes many configurations, such as your MAC address.
+The purpose of nvmutil project, is precisely to allow you to change your
+MAC address. Many other useful features are also provided.
+
+Intel defines this as the *Gigabit Ethernet Non-Volative Memory* or
+just *NVM* for short. It is a 128-byte section, consisting of 64
+words that are 2 bytes, stored in little-endian byte order.
+
+Newer Intel PHYs define an *extended* area, which starts
+immediately after the main one, but the `nvmutil` program
+does not modify or manipulate these in any way.
+
+The final word in the NVM section is the *checksum*; all words
+must add up, truncated, to the value `0xBABA`. The hardware
+itself does not calculate or validate this, and will in
+fact work nicely, but software such as Linux will check
+that this is correct. If the checksum is invalid, your
+kernel will refuse to make use of the NIC.
+
+This NVM section is the first 128 bytes of a 4KB region in flash.
+This 4KB region is then repeated, to make an 8KB region in
+flash, known as the *GbE region*. In `nvmutil`, the first part
+is referred to as *part 0* and the second part as *part 1*.
+
+How to obtain the GbE file
+==========================
+
+The chip containing your BIOS/UEFI firmware (or coreboot) has
+it, if you have an Intel PHY for gigabit ethernet.
+
+The sections below will teach you how to obtain the GbE file,
+containing your NIC's configuration. This is the part that
+many people will struggle with, so we will dedicated an
+entire next section to it:
+
+Use flashrom
+------------
+
+If you wish to operate on the GbE section that's already
+flashed, you should *dump* the current full ROM image.
+If you already have a ROM image, you do not need to dump
+it, so you can skip this section.
+
+Download flashrom here:
+
+* <https://flashrom.org/>
+
+Using recent flashrom versions, you can extract this region. If
+your regions are unlocked, you can run flashrom on the target
+system, like so:
+
+       flashrom -p internal -r rom.bin
+
+If your system has two flash chips, the GbE region is usually
+stored on SPI1 (not SPI2). Otherwise, it may be that you have
+a single-flash setup. In that case, it's recommended to dump
+both chips, as `spi1.rom` and `spi2.rom`; you can then cat
+them together:
+
+       cat spi1.rom spi2.rom > rom.bin
+
+If your GbE region is locked (per IFD settings), you can dump
+and flash it using external flashing equipment. The GNU Boot
+project has a handy guide for this; it can be used for reading
+from and writing to the chip. Please consult the guide
+in the documentation at docs/install/spi.md, or read it live
+on this URL:
+
+<https://www.gnu.org/software/gnuboot/web/docs/install/spi.html>
+
+If you're using an external programmer, the `-p internal`
+option should be changed accordingly. Read flashrom
+documentation, and make sure you have everything
+properly configured.
+
+Use ifdtool
+-----------
+
+NOTE: This has only been tested on systems that use IFDv1
+(Intel Flash Descriptor, version 1). This distinction, between
+v1 and v2, is made in the `ifdtool` source code, which you
+should read if you're interested. Intel`s v2 specification
+has more regions in it, whereas v1 systems usually
+defined: IFD, GbE, PD, ME and BIOS regions.
+
+The `ifdtool` program is a powerful tool, allowing you to
+manipulate Intel Flash Descriptors. It's part of coreboot,
+available in the `coreboot.git` repository
+under `util/ifdtool/`. Just go in there and build it
+with `make`, to get an ifdtool binary.
+
+To make internal flashing possible later on, you might do:
+
+       ifdtool --unlock rom.bin
+
+Running this command will create a modified image,
+named `rom.bin.new`. This file will have all regions set
+to read-write, per configuration in the Intel Flash Descriptor.
+
+In addition to unlocked regions, you may wish to *neuter* the
+Intel Management Engine, removing all the nasty spying features
+from it, using `me_cleaner`. See:
+
+* <https://github.com/corna/me_cleaner>
+* Also available in `coreboot.git`, undir `util/`
+
+The `me_cleaner` program is outside the scope of this
+article, so you should read their documentation.
+
+Now run this:
+
+       ifdtool -x rom.bin
+
+Several files will be created, and the one you need to
+operate on is named `flashregion_3_gbe.bin` so please
+ensure that you have this file.
+
+Read the notes below about how to use the `nvmutil` program,
+operating on this file. When you're done, you can insert the
+modified GbE file back into your ROM image, like so:
+
+       ifdtool -i gbe:flashregion_3_gbe.bin rom.bin
+
+This will create the file `rom.bin.new`, which contains
+your modified GbE section with the NVM images inside; this
+includes your MAC address.
+
+Refer to flashrom documentation. You may flash the new ROM
+like so, if running on the same system and the regions are
+read-write:
+
+       flashrom -p internal -w rom.bin.new
+
+Newer versions of flashrom support flashing just the specified
+region, like so:
+
+       flashrom -p internal --ifd -i gbe -w rom.bin.new
+
+If you're running flashrom from host CPU on the target
+system, and it's dual flash, you can just flash the
+concatenated image, which you created earlier by running
+the `cat` program; dual-IC flash configurations appear to
+your operating system as one large flash area, as though
+it were a single chip.
+
+If you're using an external programmer, you should change
+the `-p internal` parameter to something else. In this
+situation, you should re-split the file accordingly, if
+you have a dual-IC flash set, like so:
+
+       dd if=rom.bin.new of=spi2.rom bs=1M skip=8
+       dd if=rom.bin.new of=spi1.rom bs=1M count=8
+
+These files would then be flashed externally, separately,
+using an external programmer.
+
+The *above* example (using `dd`) is for setups with 12MB
+flash, where you have 8MB as SPI1 and 4MB as SPI2. SPI1
+would contain the IFD, and SPI2 is the upper flash area
+containing your bootblock; GbE is probably located in
+SPI1. You should adjust the above parameters, according
+to your configuration.
+
+How to compile source code
+==========================
+
+The nvmutil source code is located under `util/nvmutil/` in the
+GNU Boot repository. A makefile is included there, for you to build an
+executable.
+
+The nvmutil programs will work just fine, on any modern BSD Unix operating
+system, or unix-like system such as Linux.
+
+You must be sure to have toolchains installed, for
+building; a normal libc, C compiler and linker should be enough.
+GCC and LLVM have all these things included, so use whichever one
+you want.
+
+If the code is compiled on OpenBSD,
+[pledge(2)](https://man.openbsd.org/pledge.2) is used.
+This is done with an `ifdef` rule, so that the code still compiles
+on other systems. When the `dump` command is specified, pledge
+will use these promises: `stdio rpath`. When any other command
+is used, these pledge promises will be used: `stdio wpath`.
+
+The `nvmutil` software has been build-tested on `Clang`, `GCC`
+and `tcc`. Only standard library functions (plus `err.h`) are
+used, so you don't need any extra libraries.
+
+How to compile it
+-----------------
+
+First, ensure that the current working directory is your
+copy of the nvmutil source code!
+
+You may run this in your terminal:
+
+       make
+
+This will result in a binary being created named `nvm`.
+Install this to wherever you want, such as `/usr/bin` (or
+whatever is in your `$PATH` for userspace programs).
+
+TODO: Add `make install` to the Makefile, portably.
+
+How to use nvmutil
+==================
+
+You run it, passing as argument the path to a file, and you run
+commands on that file. This section will tell you how to
+perform various tasks, by using these commands.
+
+In these examples, it is assumed that you have installed
+the `nvm` binary to somewhere in your `$PATH`. If you haven't
+done that, you could still run it in cwd for instance:
+
+       ./nvm bla bla bla
+
+Exit status
+-----------
+
+The `nvmutil` program uses `errno` extensively. The best error
+handling is done this way, the Unix way. Error handling is extremely
+strict, in nvmutil; on program exit, the errno message is printed (if not
+zero) and the value of errno is returned (upon exit from `int main`).
+
+The `main` function always returns `errno`, no matter what. This style
+of programming (set errno and return) is a very old fashioned way of
+doing things, and in many cases it is the most *correct* way.
+
+This is why we say `zero status` and `non-zero status` in Unix
+programs, when we talk about exit status. Zero is success, and
+anything above zero is fail; errno is zero by default, unless
+set, and it will always be set to a value above zero (if set).
+
+All commands (except `dump`) require read and write access. The `dump`
+command only requires read access on files. Where sufficient permission
+is not given (read and/or write), nvmutil will exit with non-zero status.
+
+Non-zero status will also be returned, if the target file is *not*
+of size *8KB*.
+
+Additional rules regarding exit status shall apply, depending on
+what command you use. Commands are documented in the following sections:
+
+Change MAC address
+------------------
+
+The `nvm` program lets you change the MAC address. It sets
+a valid checksum, after changing the MAC address. This program
+operates on *both* NVM parts, but it will only modify a given
+part if the existing checksum is correct. It will exit with zero
+status if at least one part is modified; otherwise, it will
+exit with non-zero status.
+
+The following rules are enforced in code:
+
+* User cannot specify multicast addresses
+* User cannot specify `00:00:00:00:00:00`
+* When generating random addresses, if the right
+  most nibble of the left-most byte is `?` (random),
+  nvmutil will (in code) force the generated MAC
+  address to be local (not global), and will prevent
+  a multicast address from being generated.
+
+A multicast address is invalid because it represents
+multiple devices; you must specify a unicast address.
+A global address is one uniquely assigned by the vendor,
+and a local address is an overridden one. You *can* set
+global MAC addresses in nvmutil, for example if you are
+simply copying what was officially assigned to your NIC,
+you can do that. For example, if your MAC address
+was `00:de:ad:be:ef:69` as assigned by the manufacturer,
+which is a global unicast MAC address, you would type:
+
+       nvm gbe.bin setmac 00:de:ad:be:ef:69
+
+How to use (the MAC address in just an example):
+
+       nvm gbe.bin setmac 00:de:ad:be:ef:00
+
+You can also set random MAC addresses:
+
+       nvm gbe.bin setmac ??:??:??:??:??:??
+
+In this example, every character is random. However, you
+can mix and match random characters with static ones. For
+example:
+
+       nvm gbe.bin setmac 00:1f:16:??:??:??
+
+You can also pass it without a MAC address:
+
+       nvm gbe.bin setmac
+
+If you only type `setmac` without specifying a MAC address,
+it will do the same thing as `setmac ??:??:??:??:??:??`.
+
+This will set the last three bytes randomly, while the
+MAC address would begin with `00:1f:16`.
+
+The *reason* nvmutil doesn't alter a part with an existing
+invalid checksum, is precisely so that if the algorithm
+changes in future Intel PHYs, nvmutil will just fail and
+not modify your file. This is because the checksum would
+then be invalid, at all times. However, correct NVM parts
+with otherwise invalid checksums do exist, and can be
+corrected if you use the `setchecksum` command
+in `nvmutil`. It is common for vendor gbe files to contain
+one valid part and one invalid part, per checksum rules.
+
+Verify checksums (and show MAC addresses)
+-----------------------------------------
+
+This command *only* requires *read* access on files.
+
+The `nvm` program can show a hexdump of both NVM parts, and
+tell you whether each one is valid (as per checksum calculation).
+It also prints the MAC address from each part.
+
+How to use:
+
+       nvm gbe.bin dump
+
+NOTE: This will exit with zero status if at least one part
+contains a valid checksum. If both parts are invalid, nvmutil
+will exit with non-zero status.
+
+Copy part
+---------
+
+This command requires read *and* write access on files.
+
+The `nvm` program can copy one NVM part to another. It copies
+the *entire* 4KB part, within the 8KB file.
+
+Overwrite part 0 with the contents of part 1:
+
+       nvm gbe.bin copy 1
+
+Overwrite part 1 with the contents of part 0:
+
+       nvm gbe.bin copy 0
+
+NOTE: If the part to be copied has a bad checksum, no operation
+will be performed, and nvmutil will exit with non-zero status.
+Otherwise, it will (if all other conditions are met) exit with
+zero status.
+
+Swap parts
+----------
+
+This command requires read *and* write access on files.
+
+The `nvm` program can swap both 4KB parts in the GbE
+file. It does this, via simple XOR swaps.
+
+How to use:
+
+       nvm gbe.bin swap
+
+NOTE: This operation will be aborted if BOTH checksums
+are invalid. This is to guard against accidentally
+using `nvmutil` on the wrong file.
+
+If *at least one* part is valid, nvmutil will return
+with zero exit status. If both parts are invalid, it will
+return non-zero.
+
+Set valid checksum
+------------------
+
+This command requires read *and* write access on files.
+
+The `nvm` program can calculate and sets a valid checksum, on
+the desired NVM part. Usage:
+
+Fix part 0:
+
+       nvm gbe.bin setchecksum 0
+
+Fix part 1:
+
+       nvm gbe.bin setchecksum 1
+
+*WARNING: NO validity checks are performed. This will simply
+set the checksum. There is no feasible way to guard against
+use on the wrong file, unlike with the other commands. Please
+make SURE you're running this on the correct file!*
+
+Set invalid checksum
+--------------------
+
+This command requires read *and* write access on files.
+
+The `nvm` program can intentionally set an invalid checksum, on
+the desired NVM part. Usage:
+
+Invalidate part 0:
+
+       nvm gbe.bin brick 0
+
+Invalidate part 1:
+
+       nvm gbe.bin brick 1
+
+NOTE: If the part already has an invalid checksum, no operation
+will be performed, and nvmutil will exit with non-zero status.
+This is to guard against `nvmutil` being used on the wrong file.
+
+This may be desirable, if you've made modifications to both
+parts but you want to guarantee that only one of them is
+used. Also, the `setmac` command will only operate on
+parts that already have a valid checksum, so you could
+run `brick` before running `setmac` (or run it afterwards).
+
+The Linux kernel's `e1000` driver will refuse to initialise
+Intel gigabit NICs that don't have a valid checksum. This
+is software-defined, and not enforced by the hardware.
+
+LICENSE
+=======
+
+The `nvmutil` software and documentation are released under the following
+terms:
+
+Copyright 2022-2023 Leah Rowe
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/util/nvmutil/nvm b/util/nvmutil/nvm
new file mode 100755
index 
0000000000000000000000000000000000000000..40367ac14d755c1d7f51ef3daa3f67c78ba09401
GIT binary patch
literal 17944
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
F_TP?Kz)=7I

literal 0
HcmV?d00001

diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c
new file mode 100644
index 0000000..35abbfa
--- /dev/null
+++ b/util/nvmutil/nvmutil.c
@@ -0,0 +1,281 @@
+/* SPDX-License-Identifier: MIT */
+/* SPDX-FileCopyrightText: 2022, 2023 Leah Rowe <leah@libreboot.org> */
+/* SPDX-FileCopyrightText: 2023 Riku Viitanen <riku.viitanen@protonmail.com> */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void cmd_setchecksum(void), cmd_brick(void), cmd_copy(void), 
writeGbeFile(void),
+    cmd_dump(void), cmd_setmac(void), readGbeFile(void), showmac(int partnum),
+    hexdump(int partnum), handle_endianness(int partnum), openFiles(const char 
*path);
+int macAddress(const char *strMac, uint16_t *mac), validChecksum(int partnum);
+uint8_t hextonum(char chs), rhex(void);
+
+#define COMMAND argv[2]
+#define MAC_ADDRESS argv[3]
+#define PARTNUM argv[3]
+#define SIZE_4KB 0x1000
+
+uint16_t buf16[SIZE_4KB], mac[3] = {0, 0, 0};
+uint8_t *buf = (uint8_t *) &buf16;
+size_t nf = 128, gbe[2];
+uint8_t nvmPartModified[2] = {0, 0}, skipread[2] = {0, 0};
+int e = 1, flags = O_RDWR, rfd, fd, part, gbeFileModified = 0;
+
+const char *strMac = NULL, *strRMac = "??:??:??:??:??:??", *filename = NULL;
+
+typedef struct op {
+       char *str;
+       void (*cmd)(void);
+       int args;
+} op_t;
+op_t op[] = {
+{ .str = "dump", .cmd = cmd_dump, .args = 3},
+{ .str = "setmac", .cmd = cmd_setmac, .args = 3},
+{ .str = "swap", .cmd = writeGbeFile, .args = 3},
+{ .str = "copy", .cmd = cmd_copy, .args = 4},
+{ .str = "brick", .cmd = cmd_brick, .args = 4},
+{ .str = "setchecksum", .cmd = cmd_setchecksum, .args = 4},
+};
+void (*cmd)(void) = NULL;
+
+#define ERR() errno = errno ? errno : ECANCELED
+#define err_if(x) if (x) err(ERR(), "%s", filename)
+
+#define xopen(f,l,p) if (opendir(l) != NULL) err(errno = EISDIR, "%s", l); \
+    if ((f = open(l, p)) == -1) err(ERR(), "%s", l); \
+    if (fstat(f, &st) == -1) err(ERR(), "%s", l)
+
+#define word(pos16, partnum) buf16[pos16 + (partnum << 11)]
+#define setWord(pos16, p, val16) if ((gbeFileModified = 1) && \
+    word(pos16, p) != val16) nvmPartModified[p] = 1 | (word(pos16, p) = val16)
+
+int
+main(int argc, char *argv[])
+{
+       if (argc < 3) {
+               fprintf(stderr, "USAGE:\n");
+               fprintf(stderr, " %s FILE dump\n", argv[0]);
+               fprintf(stderr, " %s FILE setmac [MAC]\n", argv[0]);
+               fprintf(stderr, " %s FILE swap\n", argv[0]);
+               fprintf(stderr, " %s FILE copy 0|1\n", argv[0]);
+               fprintf(stderr, " %s FILE brick 0|1\n", argv[0]);
+               fprintf(stderr, " %s FILE setchecksum 0|1\n", argv[0]);
+               err(errno = ECANCELED, "Too few arguments");
+       }
+       flags = (strcmp(COMMAND, "dump") == 0) ? O_RDONLY : flags;
+       filename = argv[1];
+#ifdef __OpenBSD__
+       err_if(unveil("/dev/urandom", "r") == -1);
+       err_if(unveil(filename, flags == O_RDONLY ? "r" : "rw") == -1);
+       err_if(pledge(flags == O_RDONLY ? "stdio rpath" : "stdio rpath wpath",
+           NULL) == -1);
+#endif
+       openFiles(filename);
+#ifdef __OpenBSD__
+       err_if(pledge("stdio", NULL) == -1);
+#endif
+
+       for (int i = 0; i < 6; i++)
+               if (strcmp(COMMAND, op[i].str) == 0)
+                       if ((cmd = argc >= op[i].args ? op[i].cmd : NULL))
+                               break;
+       if (cmd == cmd_setmac)
+               strMac = (argc > 3) ? MAC_ADDRESS : strRMac;
+       else if ((cmd != NULL) && (argc > 3))
+               err_if((errno = (!((part = PARTNUM[0] - '0') == 0 || part == 1))
+                   || PARTNUM[1] ? EINVAL : errno));
+       err_if((errno = (cmd == NULL) ? EINVAL : errno));
+
+       readGbeFile();
+       (*cmd)();
+
+       if ((gbeFileModified) && (flags != O_RDONLY) && (cmd != writeGbeFile))
+               writeGbeFile();
+       err_if((errno != 0) && (cmd != cmd_dump));
+       return errno;
+}
+
+void
+openFiles(const char *path)
+{
+       struct stat st;
+       xopen(fd, path, flags);
+       if ((st.st_size != (SIZE_4KB << 1)))
+               err(errno = ECANCELED, "File `%s` not 8KiB", path);
+       xopen(rfd, "/dev/urandom", O_RDONLY);
+       errno = errno != ENOTDIR ? errno : 0;
+}
+
+void
+readGbeFile(void)
+{
+       nf = ((cmd == writeGbeFile) || (cmd == cmd_copy)) ? SIZE_4KB : nf;
+       skipread[part ^ 1] = (cmd == cmd_copy) | (cmd == cmd_setchecksum)
+           | (cmd == cmd_brick);
+       gbe[1] = (gbe[0] = (size_t) buf) + SIZE_4KB;
+       for (int p = 0; p < 2; p++) {
+               if (skipread[p])
+                       continue;
+               err_if(pread(fd, (uint8_t *) gbe[p], nf, p << 12) == -1);
+               handle_endianness(p);
+       }
+}
+
+void
+cmd_setmac(void)
+{
+       if (macAddress(strMac, mac))
+               err(errno = ECANCELED, "Bad MAC address");
+       for (int partnum = 0; partnum < 2; partnum++) {
+               if (!validChecksum(part = partnum))
+                       continue;
+               for (int w = 0; w < 3; w++)
+                       setWord(w, partnum, mac[w]);
+               cmd_setchecksum();
+       }
+}
+
+int
+macAddress(const char *strMac, uint16_t *mac)
+{
+       uint64_t total = 0;
+       if (strnlen(strMac, 20) == 17) {
+       for (uint8_t h, i = 0; i < 16; i += 3) {
+               if (i != 15)
+                       if (strMac[i + 2] != ':')
+                               return 1;
+               int byte = i / 3;
+               for (int nib = 0; nib < 2; nib++, total += h) {
+                       if ((h = hextonum(strMac[i + nib])) > 15)
+                               return 1;
+                       if ((byte == 0) && (nib == 1))
+                               if (strMac[i + nib] == '?')
+                                       h = (h & 0xE) | 2; /* local, unicast */
+                       mac[byte >> 1] |= ((uint16_t ) h)
+                           << ((8 * (byte % 2)) + (4 * (nib ^ 1)));
+               }
+       }}
+       return ((total == 0) | (mac[0] & 1)); /* multicast/all-zero banned */
+}
+
+uint8_t
+hextonum(char ch)
+{
+       if ((ch >= '0') && (ch <= '9'))
+               return ch - '0';
+       else if ((ch >= 'A') && (ch <= 'F'))
+               return ch - 'A' + 10;
+       else if ((ch >= 'a') && (ch <= 'f'))
+               return ch - 'a' + 10;
+       return (ch == '?') ? rhex() : 16;
+}
+
+uint8_t
+rhex(void)
+{
+       static uint8_t n = 0, rnum[16];
+       if (!n)
+               err_if(pread(rfd, (uint8_t *) &rnum, (n = 15) + 1, 0) == -1);
+       return rnum[n--] & 0xf;
+}
+
+void
+cmd_dump(void)
+{
+       for (int partnum = 0, numInvalid = 0; partnum < 2; partnum++) {
+               if (!validChecksum(partnum))
+                       ++numInvalid;
+               printf("MAC (part %d): ", partnum);
+               showmac(partnum), hexdump(partnum);
+               errno = ((numInvalid < 2) && (partnum)) ? 0 : errno;
+       }
+}
+
+void
+showmac(int partnum)
+{
+       for (int c = 0; c < 3; c++) {
+               uint16_t val16 = word(c, partnum);
+               printf("%02x:%02x", val16 & 0xff, val16 >> 8);
+               printf(c == 2 ? "\n" : ":");
+       }
+}
+
+void
+hexdump(int partnum)
+{
+       for (int row = 0; row < 8; row++) {
+               printf("%07x", row << 4);
+               for (int c = 0; c < 8; c++) {
+                       uint16_t val16 = word((row << 3) + c, partnum);
+                       printf(" %02x%02x", val16 >> 8, val16 & 0xff);
+               } printf("\n");
+       }
+}
+
+void
+cmd_setchecksum(void)
+{
+       uint16_t val16 = 0;
+       for (int c = 0; c < 0x3F; c++)
+               val16 += word(c, part);
+       setWord(0x3F, part, 0xBABA - val16);
+}
+
+void
+cmd_brick(void)
+{
+       if (validChecksum(part))
+               setWord(0x3F, part, ((word(0x3F, part)) ^ 0xFF));
+}
+
+void
+cmd_copy(void)
+{
+       if ((gbeFileModified = nvmPartModified[part ^ 1] = validChecksum(part)))
+               gbe[part ^ 1] = gbe[part]; /* speedhack: copy ptr, not words */
+}
+
+int
+validChecksum(int partnum)
+{
+       uint16_t total = 0;
+       for(int w = 0; w <= 0x3F; w++)
+               total += word(w, partnum);
+       if (total == 0xBABA)
+               return 1;
+       fprintf(stderr, "WARNING: BAD checksum in part %d\n", partnum);
+       return (errno = ECANCELED) & 0;
+}
+
+void
+writeGbeFile(void)
+{
+       err_if((cmd == writeGbeFile) && !(validChecksum(0) || 
validChecksum(1)));
+       for (int p = 0, x = (cmd == writeGbeFile) ? 1 : 0; p < 2; p++) {
+               if ((!nvmPartModified[p]) && (cmd != writeGbeFile))
+                       continue;
+               handle_endianness(p^x);
+               err_if(pwrite(fd, (uint8_t *) gbe[p^x], nf, p << 12) == -1);
+       }
+       errno = 0;
+       err_if(close(fd) == -1);
+}
+
+void
+handle_endianness(int partnum)
+{
+       uint8_t *n = (uint8_t *) gbe[partnum];
+       for (size_t w = nf * ((uint8_t *) &e)[0], x = 1; w < nf; w += 2, x += 2)
+               n[w] ^= n[x], n[x] ^= n[w], n[w] ^= n[x];
+}
-- 
2.39.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]