[2/3] utils: Add helper to send mailbox request
diff mbox series

Message ID 20181203030035.26487-2-sam@mendozajonas.com
State Superseded
Headers show
Series
  • [1/3] discover/platform-powerpc: read bootdev config from IPMI boot mailbox
Related show

Commit Message

Samuel Mendoza-Jonas Dec. 3, 2018, 3 a.m. UTC
A simple script to set, display, and clear a BMC's boot initiator mailbox.

Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
---
 utils/ipmi-mailbox-config.py | 167 +++++++++++++++++++++++++++++++++++
 1 file changed, 167 insertions(+)
 create mode 100755 utils/ipmi-mailbox-config.py

Patch
diff mbox series

diff --git a/utils/ipmi-mailbox-config.py b/utils/ipmi-mailbox-config.py
new file mode 100755
index 00000000..b1060797
--- /dev/null
+++ b/utils/ipmi-mailbox-config.py
@@ -0,0 +1,167 @@ 
+#!/usr/bin/env python3
+
+import argparse
+import subprocess
+
+def send_block_read_command(hostname, username, password, index, dry_run):
+
+    if hostname is None and dry_run:
+        hostname = "<hostname>"
+
+    cmd = "ipmitool -I lanplus -H " + hostname
+    if username is not None:
+        cmd = cmd + " -U " + username
+    if password is not None:
+        cmd = cmd + " -P " + password
+
+    # chassis netfn, get-sys-boot-options, parameter 7, set-sel, block-sel
+    cmd = cmd + " raw 0x00 0x09 0x07 " + hex(index) + " 0x00 "
+
+    print(cmd)
+    if not dry_run:
+        rc = subprocess.call(cmd, shell=True)
+        if rc != 0:
+            print("Command returned error: {}".format(rc))
+
+def send_block_command(hostname, username, password, block, index, dry_run):
+
+    if hostname is None and dry_run:
+        hostname = "<hostname>"
+
+    cmd = "ipmitool -I lanplus -H " + hostname
+    if username is not None:
+        cmd = cmd + " -U " + username
+    if password is not None:
+        cmd = cmd + " -P " + password
+
+    # chassis netfn, set-sys-boot-options, parameter 7, set-sel, block-sel
+    cmd = cmd + " raw 0x00 0x08 0x07 " + hex(index) + " " + block
+
+    print(cmd)
+    if not dry_run:
+        rc = subprocess.call(cmd, shell=True)
+        if rc != 0:
+            print("Command returned error: {}".format(rc))
+
+def construct_buffer(config, max_blocks):
+
+    # Add IBM's IANA prefix
+    raw = bytes([0x02, 0x00, 0x00]) + config.encode('ascii')
+
+    n_blocks = int(len(raw) / 16)
+    if len(raw) % 16 != 0:
+        n_blocks += 1
+
+    if n_blocks > 16:
+        print("ERROR: buffer would require {} blocks" +
+                "which is more than allowed (16)".format(n_blocks))
+        return None
+
+    if n_blocks > max_blocks:
+        print("ERROR: buffer would require {} blocks" +
+                "which is more than max_blocks ({})".format(n_blocks, max_blocks))
+        return None
+
+    if n_blocks > 5:
+        print("Warning: buffer would require {} blocks" +
+                "which is more than some BMCs support".format(n_blocks))
+
+
+    blocks = []
+    rem = len(raw)
+    for i in range(n_blocks):
+        block = ""
+        if rem >= 16:
+            last = 16
+        else:
+            last = rem
+
+        for j in range(16):
+            if j < last:
+                block += "{:#02x} ".format(raw[i * 16 + j])
+            else:
+                # Pad out to 16 bytes
+                block += "0x00 "
+
+
+        blocks.append(block)
+        rem -= last
+
+    if n_blocks < max_blocks:
+        for i in range(max_blocks - n_blocks):
+            blocks.append("0x00 0x00 0x00 0x00 " +
+                          "0x00 0x00 0x00 0x00 " +
+                          "0x00 0x00 0x00 0x00 " +
+                          "0x00 0x00 0x00 0x00")
+
+    return blocks
+
+def construct_empty_buffer(max_blocks):
+
+    blocks = []
+    for i in range(max_blocks):
+        blocks.append("0x00 0x00 0x00 0x00 " +
+                      "0x00 0x00 0x00 0x00 " +
+                      "0x00 0x00 0x00 0x00 " +
+                      "0x00 0x00 0x00 0x00")
+
+    return blocks
+
+def main():
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-b", "--bmc-hostname")
+    parser.add_argument("-u", "--username")
+    parser.add_argument("-p", "--password")
+    parser.add_argument("-n", "--dry-run", action="store_true")
+    parser.add_argument("-c", "--config")
+    parser.add_argument("-x", "--clear", action="store_true")
+    parser.add_argument("-d", "--dump", action="store_true")
+    parser.add_argument("-m", "--max-blocks")
+
+    args = parser.parse_args()
+
+    if not args.dry_run and args.bmc_hostname is None:
+        print("No hostname specified!")
+        return -1
+
+    if args.config and args.clear:
+        print("Can't specify --config and --clear together")
+        return -1
+
+    if args.max_blocks:
+        n_blocks = int(args.max_blocks)
+    else:
+        n_blocks = 16
+
+
+    if args.config or args.clear:
+        if args.config:
+            blocks = construct_buffer(args.config, int(args.max_blocks))
+        if args.clear:
+            blocks = construct_empty_buffer(int(args.max_blocks))
+        if blocks is None:
+            print("Failed to construct buffer")
+            return -1
+
+        print("{} blocks to send".format(len(blocks)))
+        print("---------------------------------------")
+        for i in range(len(blocks)):
+            try:
+                send_block_command(args.bmc_hostname, args.username, args.password,
+                        blocks[i], i, args.dry_run)
+            except Exception as e:
+                print(e)
+                print("Error sending block {}".format(i))
+                return -1
+            i += 1
+
+    if args.dump:
+        print("\nReading {} blocks".format(n_blocks))
+        print("---------------------------------------")
+        for i in range(n_blocks):
+            send_block_read_command(args.bmc_hostname, args.username,
+                    args.password, i, args.dry_run)
+
+if __name__ == "__main__":
+    main()