Add UFS provisioning script

This change adds a generic UFS provisioning script. It does two
things: sets the bRefClkFreq attribute, and writes the configuration
descriptor to create a single LUN covering the entire storage space
of the device.

Note: The kernel support for provisioning UFS has not yet landed
upstream, so this script is still a work-in-progress. Once the
kernel support lands, this script can be given some teeth.

BUG=b:74036926
TEST=Run script manually on Cheza, verify output bytes look good.

Change-Id: I42a75cdef69ecb1e37e2f876dc127a6db63d3c4a
Reviewed-on: https://chromium-review.googlesource.com/1142469
Commit-Ready: Evan Green <evgreen@chromium.org>
Tested-by: Evan Green <evgreen@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
diff --git a/factory_ufs_init.sh b/factory_ufs_init.sh
new file mode 100644
index 0000000..6732f4b
--- /dev/null
+++ b/factory_ufs_init.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script is run in the factory, and provisions a UFS disk with a single
+# LUN that spans the entire region.
+
+# Try to find a UFS controller.
+UFS_DEV="$(find /sys/devices -name '*.ufshc' -type d)"
+
+# Set a default for bRefClkFreq, which can be one of 0 (19.2MHz),
+# 1 (26MHz), 2 (38.4MHz), or 3 (52MHz). The default is arbitrary.
+: ${REF_CLK:=0}
+
+# Read in the board-specific file if supplied.
+DIRNAME="$(dirname "$(readlink -f "$0")")"
+if [ -s "${DIRNAME}/factory_ufs_config.sh" ]; then
+  . "${DIRNAME}/factory_ufs_config.sh"
+fi
+
+if ! [ -d "${UFS_DEV}" ]; then
+  echo "Error: No UFS host controller found at: ${UFS_DEV}"
+  exit 1
+fi
+
+# Helper function: Convert a number like 0x12345678 into its big endian
+# hex bytes 12 34 56 78.
+int_to_bytes_be32() {
+  printf "%02x %02x %02x %02x" $((${1} >> 24)) $(((${1} >> 16) & 0xFF)) \
+    $(((${1} >> 8) & 0xFF)) $((${1} & 0xFF))
+}
+
+# First set the reference clock frequency, since if this isn't set, nothing
+# else will work.
+if [ $(($(cat "${UFS_DEV}"/attributes/reference_clock_frequency))) -ne \
+     "${REF_CLK}" ]; then
+  echo "${REF_CLK}" >"${UFS_DEV}"/attributes/reference_clock_frequency || {
+    echo "Failed to set reference clock frequency"
+    exit 1
+  }
+fi
+
+# Get the raw device capacity in 512B units.
+TOTAL_CAPACITY="$(cat "${UFS_DEV}"/geometry_descriptor/raw_device_capacity)"
+
+echo "UFS Capacity: $((TOTAL_CAPACITY / 0x200000))GB"
+
+# The segment size reports the number of 512B units in a segment.
+SEGMENT_SIZE="$(cat "${UFS_DEV}"/geometry_descriptor/segment_size)"
+
+# Get the allocation unit size, expressed in segments.
+SEGMENTS_PER_ALLOCATION_UNIT="$(cat \
+  "${UFS_DEV}"/geometry_descriptor/allocation_unit_size)"
+
+# Dimensional analysis helps with understanding how the units convert from
+# sectors to allocation units.
+#
+# TOTAL (sectors) |          (segments) | (allocation units)    allocation units
+# ----------------------------------------------------------- =
+#                 | SEG_SIZE (sectors)  | S_PER_A_U (segments)
+
+TOTAL_ALLOCATION_UNITS=$((TOTAL_CAPACITY / SEGMENT_SIZE / \
+  SEGMENTS_PER_ALLOCATION_UNIT))
+
+# Just allocate the entire disk to the first unit.
+ALLOC_UNITS="$(int_to_bytes_be32 ${TOTAL_ALLOCATION_UNITS})"
+
+# Build the configuration descriptor as a sequence of bytes.
+# This is the point at which things might change depending on
+# how the kernel support for this shapes out. The way it's looking
+# now I'm expecting a configfs file that a sequence of binary bytes
+# containing the provisioning data.
+HEADER="90 01 00 01 00 01 7F 00 00 00 00 00 00 00 00 00"
+UNIT0="01 00 00 00 ${ALLOC_UNITS} 00 0C 00 00 00 00 00 00"
+EMPTY="00 00 00 00 00 00 00 00 00 0C 00 00 00 00 00 00"
+CONFIG_DESCRIPTOR="${HEADER} ${UNIT0} ${EMPTY} ${EMPTY} ${EMPTY} ${EMPTY}\
+ ${EMPTY} ${EMPTY} ${EMPTY}"
+
+# TODO(evgreen): I've created both the ASCII and binary versions of this.
+# Remove one of them depending on how upstream kernel support
+# shakes out.
+
+# Create the ASCII version.
+CONFIG_ASCII="$(echo "0x${CONFIG_DESCRIPTOR}" | sed 's/ / 0x/g')"
+
+# Create a binary version of the config descriptor.
+CONFIG_BINARY="$(echo " ${CONFIG_DESCRIPTOR}" | sed 's/ /\\x/g')"
+
+# TODO(evgreen): Send the output to the kernel, which might look something like:
+# printf "${CONFIG_BINARY}" >\
+# /path/to/configfs/ufshc.80400000/sys_provision
+# But for now, just spit it to standard out.
+# Dash's built-in printf doesnt support \x, so use the external one.
+$(which printf) "${CONFIG_BINARY}" | od -tx1 -Ax
+
+# Perform a SCSI rescan to bring the device online
+echo "- - -" >"${UFS_DEV}"/host0/scsi_host/host0/scan