meta-google: gbmc-mac-config: Add package

This package allows a system to specify an IPMI FRU that contains MAC
Address information used to populated MAC addresses for specified
interfaces.

Change-Id: I457d41509da0e63db4410937b84140d4ba410b41
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/meta-google/recipes-google/networking/files/gbmc-mac-config.service b/meta-google/recipes-google/networking/files/gbmc-mac-config.service
new file mode 100644
index 0000000..fd9247d
--- /dev/null
+++ b/meta-google/recipes-google/networking/files/gbmc-mac-config.service
@@ -0,0 +1,10 @@
+[Unit]
+Before=systemd-networkd.service
+
+[Service]
+Restart=on-failure
+Type=oneshot
+ExecStart=/usr/libexec/gbmc-mac-config.sh
+
+[Install]
+WantedBy=multi-user.target
diff --git a/meta-google/recipes-google/networking/files/gbmc-mac-config.sh.in b/meta-google/recipes-google/networking/files/gbmc-mac-config.sh.in
new file mode 100644
index 0000000..a3430a6
--- /dev/null
+++ b/meta-google/recipes-google/networking/files/gbmc-mac-config.sh.in
@@ -0,0 +1,82 @@
+#!/bin/bash
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source /usr/share/ipmi-fru/lib.sh || exit
+
+eeprom="$(of_name_to_eeprom '@EEPROM@')" || exit
+
+header=()
+read_header "$eeprom" header || exit
+internal_offset=${header[$IPMI_FRU_COMMON_HEADER_INTERNAL_OFFSET_IDX]}
+if (( internal_offset == 0 )); then
+  echo "Internal offset invalid for eeprom" >&2
+  exit 1
+fi
+
+# Our MAC Address configuration lives in the internal area with a format
+#   Offset Data
+#        0 Version (Always 1)
+#        1 Type (Always 1 for MAC Address)
+#        2 Area Length in bytes (Always 32 bytes or 4 IPMI FRU sectors)
+#      3-8 MAC Address Base Octets
+#        9 Num Allocate MACs from Base
+#    10-30 Padding (Always 0xFF)
+#       31 IPMI FRU Checksum
+internal=()
+read_area "$eeprom" "$internal_offset" internal 4 || exit
+if (( internal[1] != 1 || internal[2] != 32 )); then
+  echo "Not a MAC internal region" >&2
+  exit 1
+fi
+mac=("${internal[@]:3:6}")
+num="${internal[@]:9:1}"
+macstr=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' "${mac[@]}")
+echo "Base MAC $macstr num $num" >&2
+
+rc=0
+
+# Pre-Determine if we will miss an allocation due to the number of
+# addresses the FRU actually supports.
+declare -A num_to_if=(@NUM_TO_IF@)
+for key in "${!num_to_if[@]}"; do
+  if (( key >= num )); then
+    echo "${num_to_if[$key]} at $key is out of range" >&2
+    rc=1
+  fi
+done
+
+# Write out each MAC override to the runtime networkd configuration
+for (( i=0; i<num; i++ )); do
+  intf="${num_to_if[$i]}"
+  if [ -n "$intf" ]; then
+    macstr=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' "${mac[@]}")
+    echo "Setting $intf to $macstr" >&2
+    for override in /run/systemd/network/{00,}-bmc-$intf.network.d; do
+      mkdir -p "$override"
+      printf '[Link]\nMACAddress=%s\n' "$macstr" >"$override"/50-mac.conf
+    done
+    for override in /run/systemd/network/{00,}-bmc-$intf.netdev.d; do
+      mkdir -p "$override"
+      printf '[NetDev]\nMACAddress=%s\n' "$macstr" >"$override"/50-mac.conf
+    done
+  fi
+  if (( ++mac[5] > 0xff )); then
+    echo "MAC assignment too large: ${mac[@]}" >&2
+    rc=2
+    break
+  fi
+done
+
+exit $rc
diff --git a/meta-google/recipes-google/networking/gbmc-mac-config.bb b/meta-google/recipes-google/networking/gbmc-mac-config.bb
new file mode 100644
index 0000000..6ca4c79
--- /dev/null
+++ b/meta-google/recipes-google/networking/gbmc-mac-config.bb
@@ -0,0 +1,50 @@
+SUMMARY = "Configures MAC addresses on a gBMC system"
+PR = "r1"
+LICENSE = "Apache-2.0"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
+
+inherit systemd
+
+SRC_URI += " \
+  file://gbmc-mac-config.service \
+  file://gbmc-mac-config.sh.in \
+  "
+
+S = "${WORKDIR}"
+
+RDEPENDS_${PN} += " \
+  bash \
+  ipmi-fru-sh \
+  "
+
+FILES_${PN} += "${systemd_unitdir}"
+
+SYSTEMD_SERVICE_${PN} += "gbmc-mac-config.service"
+
+GBMC_MAC_EEPROM_OF_NAME ?= ""
+
+# Maps the MAC address offset from the base address to an interface name
+# in bash associative array syntax.
+#   Ex. "[0]=eth0 [2]=eth2"
+GBMC_MAC_IF_MAP ?= ""
+
+do_install_append() {
+  if [ -z '${GBMC_MAC_EEPROM_OF_NAME}' ]; then
+    echo 'Missing GBMC_MAC_EEPROM_OF_NAME' >&2
+    exit 1
+  fi
+
+  # Build time dictionary sanity check
+  bash -c 'declare -A dict=(${GBMC_MAC_IF_MAP})'
+
+  sed gbmc-mac-config.sh.in \
+    -e 's#@EEPROM@#${GBMC_MAC_EEPROM_OF_NAME}#' \
+    -e 's#@NUM_TO_IF@#${GBMC_MAC_IF_MAP}#' \
+    >gbmc-mac-config.sh
+
+  install -d -m0755 ${D}${libexecdir}
+  install -m0755 gbmc-mac-config.sh ${D}${libexecdir}/
+
+  install -d -m0755 ${D}${systemd_system_unitdir}
+  install -m0644 gbmc-mac-config.service ${D}${systemd_system_unitdir}/
+}
diff --git a/meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend b/meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend
index 37655b9..520aeeb 100644
--- a/meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend
+++ b/meta-google/recipes-phosphor/images/obmc-phosphor-image.bbappend
@@ -8,3 +8,5 @@
 OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = " gbmc-iperf3"
 OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = \
   '${@"" if not d.getVar("GBMC_NCSI_IF_NAME") else " gbmc-ncsi-config"}'
+OBMC_IMAGE_EXTRA_INSTALL_append_gbmc = \
+  '${@"" if not d.getVar("GBMC_MAC_EEPROM_OF_NAME") else " gbmc-mac-config"}'