meta-yadro: vegman: add default FRU handler

default-fru-vegman recipe provides script to apply hardware configuration
on first boot based on FRU EEPROM content.

Signed-off-by: Andrei Kartashev <a.kartashev@yadro.com>
Change-Id: I7a17c519259e4bb56a2a00900894ffdbd4edd2f6
diff --git a/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler.bb b/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler.bb
new file mode 100644
index 0000000..5329d6e
--- /dev/null
+++ b/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler.bb
@@ -0,0 +1,23 @@
+SUMMARY = "Apply default configuration from baseboard FRU"
+DESCRIPTION = "Provide baseboard FRU EEPROM handlers to apply platform configuration on system boot"
+
+inherit systemd
+
+SYSTEMD_SERVICE:${PN} = " \
+        baseboard-fru-handler.service \
+    "
+
+RDEPENDS:${PN} = "bash u-boot-fw-utils"
+S = "${WORKDIR}"
+SRC_URI = " \
+        file://baseboard-fru-handler.sh \
+        file://baseboard-fru-handler.service \
+    "
+
+LICENSE = "Apache-2.0"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
+
+do_install() {
+    install -m 0755 ${S}/baseboard-fru-handler.sh -D -t ${D}${bindir}
+    install -m 0644 ${S}/baseboard-fru-handler.service -D -t ${D}${systemd_system_unitdir}
+}
diff --git a/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler/baseboard-fru-handler.service b/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler/baseboard-fru-handler.service
new file mode 100644
index 0000000..19d9af7
--- /dev/null
+++ b/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler/baseboard-fru-handler.service
@@ -0,0 +1,17 @@
+[Unit]
+Description=Enforce Static MAC addr mapping and hostname setting
+Wants=xyz.openbmc_project.FruDevice.service
+After=xyz.openbmc_project.FruDevice.service
+Wants=systemd-hostnamed.service
+After=systemd-hostnamed.service
+Before=xyz.openbmc_project.Network.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/bin/env baseboard-fru-handler.sh
+SyslogIdentifier=baseboard-fru-handler
+
+[Install]
+WantedBy=network.target
+
diff --git a/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler/baseboard-fru-handler.sh b/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler/baseboard-fru-handler.sh
new file mode 100644
index 0000000..f52acd4
--- /dev/null
+++ b/meta-yadro/meta-vegman/recipes-yadro/fru/vegman-fru-handler/baseboard-fru-handler.sh
@@ -0,0 +1,180 @@
+#!/bin/bash -eu
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2020-2021 YADRO
+
+log_msg() {
+    echo "$@"
+}
+
+log_err() {
+    echo "$@" >&2
+}
+
+read_hw_mac() {
+    local iface="$1"
+    cat /sys/class/net/"${iface}"/address 2>/dev/null ||:
+}
+
+set_hw_mac() {
+    local iface="$1"
+    local mac="$2"
+    local up=""
+    if ip link show dev "${iface}" | grep -q "${iface}:.*\<UP\>" 2>/dev/null; then
+        up=true
+    fi
+
+    if [ "${up}" = true ]; then
+        ip link set dev "${iface}" down ||:
+    fi
+    ip link set dev "${iface}" address "${mac}" ||:
+    if [ "${up}" = true ]; then
+        ip link set dev "${iface}" up ||:
+    fi
+}
+
+set_fw_env_mac() {
+    local iface="$1"
+    local mac="$2"
+    local envname=""
+    case "${iface}" in
+        eth0)
+            envname="ethaddr"
+        ;;
+        eth1)
+            envname="eth1addr"
+        ;;
+        *)
+            return 1
+        ;;
+    esac
+    if ! fw_setenv "$envname" "$mac"; then
+        return 1
+    fi
+}
+
+######## MAIN ########
+FRU_DBUS_SERVICE="xyz.openbmc_project.FruDevice"
+FRU_DBUS_OBJECT_TEMPLATE="\/xyz.*_Motherboard"
+FRU_DBUS_INTERFACE="xyz.openbmc_project.FruDevice"
+fru_dbus_object=""
+
+######## Parse D-bus data ########
+n=1
+while true; do
+    fru_dbus_object=$(busctl tree ${FRU_DBUS_SERVICE} 2>/dev/null |
+        sed -n "s/^.*\(${FRU_DBUS_OBJECT_TEMPLATE}\).*$/\1/p"
+      ) && [ -n "${fru_dbus_object}" ] && break
+    if [ ${n} -lt 15 ]; then
+        n=$((n+1))
+        sleep 1
+    else
+        log_err "Failed to find baseboard FRU object"
+        exit 1
+    fi
+done
+
+dbusData=$(dbus-send --system --print-reply=literal \
+            --dest=${FRU_DBUS_SERVICE} \
+            "${fru_dbus_object}" \
+            org.freedesktop.DBus.Properties.GetAll \
+            string:${FRU_DBUS_INTERFACE} 2>/dev/null ||:)
+
+if [ -z "${dbusData}" ]; then
+    log_err "Failed to get data from D-Bus"
+    exit 1
+fi
+
+# This awk script matches strings 010xyyyyyyyyyyyy, where x is interface index
+# and yyyyyyyyyyyy - interface mac address.
+# The output would be in form ethx=yy:yy:yy:yy:yy:yy
+macsList=$(echo "${dbusData}" | \
+    awk '/BOARD_INFO_AM[0-9]+\s+variant\s+010[0-9a-f]{13}/{
+        totalMacDigits=12
+        singleOctet=2
+        offset=4
+        printf "eth%s=", substr($3, offset, 1)
+        for(i = 1 + offset; i < totalMacDigits + offset; i += singleOctet) {
+            printf "%s", substr($3, i, singleOctet)
+            if (i <= totalMacDigits + offset - singleOctet) printf ":"
+        }
+        printf "\n"
+    }'||:)
+
+if [ -z "${macsList}" ]; then
+    log_err "Failed to get MAC address"
+    exit 1
+fi
+
+# Get hardware model name from PRODUCT_PRODUCT_NAME
+# Examples:
+#  'VEGMAN N110 Server' -> 'VEGMAN-N110'
+#  'TATLIN.ARCHIVE.XS' -> 'TATLIN-ARCHIVE-XS'
+modelName=$(echo "${dbusData}" | \
+        awk '/PRODUCT_PRODUCT_NAME\s+variant\s+/{
+                if ($3 ~ /^VEGMAN/) {
+                    printf "%s-%s", $3, $4
+                } else if ($3 ~ /^TATLIN/) {
+                    print gensub(/\./, "-", "g", $3)
+                }
+             }'||:)
+
+serialNumber=$(echo "${dbusData}" | \
+     awk '/PRODUCT_SERIAL_NUMBER\s+variant\s+/{print $3}'||:)
+if [ -z "${serialNumber}" ]; then
+    log_err "Failed to get product Serial Number"
+    exit 1
+fi
+
+# shellcheck disable=SC1091
+source /etc/os-release
+
+
+######## Check and set MAC addresses ########
+retCode=0
+IFS='
+'
+for line in ${macsList} ; do
+    ifaceName=$(echo "${line}" | cut -f 1 -d '=' || :)
+    macAddr=$(echo "${line}" | cut -f 2- -d '=' || :)
+    if [ -n "${ifaceName}" ] && [ -n "${macAddr}" ]; then
+        curMacAddr=$(read_hw_mac "${ifaceName}")
+        if [ "${macAddr}" != "${curMacAddr}" ] ; then
+            log_msg "Changing MAC address for ${ifaceName}: ${curMacAddr} -> ${macAddr}"
+            # A factory assigned address was found, and it is newly assigned.
+            # Update the active interface and save the new value to the u-boot
+            # environment.
+            if ! set_hw_mac "${ifaceName}" "${macAddr}"; then
+                log_err "Failed to set address"
+                retCode=1
+            fi
+            if ! set_fw_env_mac "${ifaceName}" "${macAddr}"; then
+                log_err "Failed to set boot env for ${ifaceName}"
+                retCode=1
+            fi
+        fi
+    fi
+
+    [ ${retCode} -eq 0 ] || exit ${retCode}
+done
+
+
+######## Check and set hostname ########
+hostname=$(hostname)
+if [ "${OPENBMC_TARGET_MACHINE}" = "${hostname}" ] ; then
+    hostname="${modelName,,}-${serialNumber}"
+    log_msg "Changing hostname to ${hostname}"
+
+    if ! hostnamectl set-hostname "${hostname}-${SN}"; then
+        log_err "Failed to set new hostname"
+        exit 1
+    fi
+fi
+
+######## Run optional scripts ########
+snmp_handler="/usr/sbin/snmpd-generate-conf.sh"
+if [ -x ${snmp_handler} ]; then
+    if ! ${snmp_handler} "${modelName}"; then
+        log_err "Failed to setup snmp"
+    fi
+fi
diff --git a/meta-yadro/meta-vegman/recipes-yadro/packagegroups/packagegroup-yadro-apps.bb b/meta-yadro/meta-vegman/recipes-yadro/packagegroups/packagegroup-yadro-apps.bb
index 4684ded..3b7e3a1 100644
--- a/meta-yadro/meta-vegman/recipes-yadro/packagegroups/packagegroup-yadro-apps.bb
+++ b/meta-yadro/meta-vegman/recipes-yadro/packagegroups/packagegroup-yadro-apps.bb
@@ -39,6 +39,7 @@
 
 SUMMARY:${PN}-system = "System software"
 RDEPENDS:${PN}-system = " \
+    vegman-fru-handler \
     ${PN}-interface \
     ${PN}-cli \
 "