cpuinfoapp: Add SST discovery feature

Retrieve Intel Speed Select Technology (SST) configuration values for
all CPUs via PECI (OS-PCode mailbox). Each CPU may have up to three
Performance Profiles (PP), each with accompanying Base Frequency (BF)
information.

Discovery is started immediately, but if no CPUs are found or any
unexpected PECI error is encountered, discovery is aborted and scheduled
for periodic retries until complete.

The profile data is published on D-Bus using two predefined interfaces:
 - xyz.openbmc_project.Control.Processor.CurrentOperationConfig, which
   is implemented on each "cpu" object in the inventory, and contains
   mutable properties for OOB configuration (modifiying properties not
   supported yet).
 - xyz.openbmc_project.Inventory.Item.Cpu.OperationConfig, which is
   implemented on separate "config" objects and contains the readonly
   properties for each performance profile.

Tested:
 - Profiled performance of PECI operations via code instrumentation
   (takes ~2 min per CPU on ast2500 during BMC boot, ~2 sec during BMC idle).
 - Validated Redfish output against Linux driver using included python
   tool.
 - Injected PECI failures in code to test error handling, and tested
   with Linux OS idling on host to make sure WOP is working.

Change-Id: I0d8ae79655dfd2880cf3bae6abe600597740df7c
Signed-off-by: Jonathan Doman <jonathan.doman@intel.com>
diff --git a/tools/sst-info.sh b/tools/sst-info.sh
new file mode 100755
index 0000000..6005948
--- /dev/null
+++ b/tools/sst-info.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# Utility to print all SST data present on D-Bus.
+# Simply searches for all objects implementing known interfaces and prints out
+# the property values on those interfaces.
+
+BUSCTL='busctl'
+XYZ='xyz.openbmc_project'
+OBJECT_MAPPER="$XYZ.ObjectMapper /xyz/openbmc_project/object_mapper $XYZ.ObjectMapper"
+CPU_INTF="$XYZ.Control.Processor.CurrentOperatingConfig"
+CONFIG_INTF="$XYZ.Inventory.Item.Cpu.OperatingConfig"
+
+trim_quotes() {
+    trim_obj=${1%\"}
+    trim_obj=${trim_obj#\"}
+    echo $trim_obj
+}
+
+get_sub_tree_paths() {
+    resp=$($BUSCTL call $OBJECT_MAPPER GetSubTreePaths sias "$1" 0 "$2" "$3" \
+           | cut -d' ' -f3-)
+    for obj in $resp
+    do
+        trim_quotes $obj
+    done
+}
+
+get_service_from_object() {
+    trim_quotes $($BUSCTL call $OBJECT_MAPPER GetObject sas "$1" "$2" "$3" \
+                  | cut -d' ' -f3)
+}
+
+get_property_names() {
+    service=$1
+    object=$2
+    intf=$3
+    $BUSCTL introspect $service $object $intf \
+        | awk '/property/ {print substr($1, 2)}'
+}
+
+get_property() {
+    service=$1
+    object=$2
+    intf=$3
+    prop=$4
+    $BUSCTL get-property $service $object $intf $prop
+}
+
+
+cpu_paths=$(get_sub_tree_paths "/" 1 "$CPU_INTF")
+for cpu_path in $cpu_paths
+do
+    service=$(get_service_from_object $cpu_path 1 $CPU_INTF)
+    echo "Found SST on $cpu_path on $service"
+    for prop in $(get_property_names $service $cpu_path $CPU_INTF)
+    do
+        echo "  $prop: $(get_property $service $cpu_path $CPU_INTF $prop)"
+    done
+
+
+    profiles=$(get_sub_tree_paths "$cpu_path" 1 "$CONFIG_INTF")
+    for profile in $profiles
+    do
+        echo
+        echo "  Found Profile $profile"
+        for prop in $(get_property_names $service $profile $CONFIG_INTF)
+        do
+            echo "    $prop: $(get_property $service $profile $CONFIG_INTF $prop)"
+        done
+    done
+done