[qcom][smc] Add qcom specific smc interface

Qualcomm's interpretation of the SMC calling conventions requires
keeping track of a request's session id returned from smc calls
in X6.  This change moves support for Qualcomm's SMC retry mechanism
from PIL to a library including storage of session id across retires
to be used by other QSEE clients.

Test: PIL authentication of the Audio DSP FW image.
Change-Id: I762e4dbb0283960d90c2da590321b0f10168f4ca
diff --git a/zircon/system/dev/fw/qcom-pil/BUILD.gn b/zircon/system/dev/fw/qcom-pil/BUILD.gn
index 6ae72b9..0bbef09 100644
--- a/zircon/system/dev/fw/qcom-pil/BUILD.gn
+++ b/zircon/system/dev/fw/qcom-pil/BUILD.gn
@@ -12,6 +12,7 @@
   deps = [
     "$zx/system/banjo/ddk-protocol-platform-bus",
     "$zx/system/banjo/ddk-protocol-platform-device",
+    "$zx/system/dev/lib/qcom",
     "$zx/system/ulib/ddk",
     "$zx/system/ulib/ddktl",
     "$zx/system/ulib/elfload",
diff --git a/zircon/system/dev/fw/qcom-pil/qcom-pil.cpp b/zircon/system/dev/fw/qcom-pil/qcom-pil.cpp
index b1d22d2..7358935 100644
--- a/zircon/system/dev/fw/qcom-pil/qcom-pil.cpp
+++ b/zircon/system/dev/fw/qcom-pil/qcom-pil.cpp
@@ -13,6 +13,7 @@
 #include <fbl/alloc_checker.h>
 #include <fbl/string_buffer.h>
 #include <fbl/unique_ptr.h>
+#include <qcom/smc.h>
 
 #include "qcom-pil.h"
 
@@ -65,8 +66,8 @@
                                 CreateSmcArgs(2, SmcArgType::kValue, SmcArgType::kBufferReadWrite),
                                 fw_[fw_n].id,  // kValue.
                                 fw_[fw_n].pa); // kBufferReadWrite.
-    status = SmcCall(&params, &result);
-    if (status != ZX_OK || result.arg0 != kSmcOk) {
+    status = qcom::SmcCall(smc_.get(), &params, &result);
+    if (status != ZX_OK || result.arg0 != qcom::kSmcOk) {
         zxlogf(ERROR, "%s metadata init failed %d/%d\n", __func__, status,
                static_cast<int>(result.arg0));
         return status;
@@ -110,8 +111,8 @@
         fw_[fw_n].id, // kValue.
         fw_[fw_n].pa, // kValue, not clear why not a kBuffer...
         total_size);  // kValue.
-    status = SmcCall(&params, &result);
-    if (status != ZX_OK || result.arg0 != kSmcOk) {
+    status = qcom::SmcCall(smc_.get(), &params, &result);
+    if (status != ZX_OK || result.arg0 != qcom::kSmcOk) {
         zxlogf(ERROR, "%s memory setup failed %d/%d\n", __func__, status,
                static_cast<int>(result.arg0));
         return status;
@@ -151,8 +152,8 @@
     // Authenticates the whole image via SMC call.
     params = CreatePilSmcParams(PilCmd::AuthAndReset, CreateSmcArgs(1, SmcArgType::kValue),
                                 fw_[fw_n].id);
-    status = SmcCall(&params, &result);
-    if (status != ZX_OK || result.arg0 != kSmcOk) {
+    status = qcom::SmcCall(smc_.get(), &params, &result);
+    if (status != ZX_OK || result.arg0 != qcom::kSmcOk) {
         zxlogf(ERROR, "%s authentication failed %d/%d\n", __func__, status,
                static_cast<int>(result.arg0));
         return status;
@@ -168,36 +169,6 @@
     return 0;
 }
 
-zx_status_t PilDevice::SmcCall(zx_smc_parameters_t* params, zx_smc_result_t* result) {
-    zxlogf(TRACE, "SMC params 0x%X 0x%lX 0x%lX 0x%lX 0x%lX 0x%lX\n", params->func_id,
-           params->arg1, params->arg2, params->arg3, params->arg4, params->arg5);
-    auto status = zx_smc_call(smc_.get(), params, result);
-    zxlogf(TRACE, "SMC results %ld 0x%lX 0x%lX 0x%lX\n", result->arg0, result->arg1, result->arg2,
-           result->arg3);
-
-    constexpr int total_retry_msecs = 2000;
-    constexpr int busy_retry_msecs = 30;
-    constexpr int busy_retries = total_retry_msecs / busy_retry_msecs;
-    int busy_retry = busy_retries;
-    while (status == ZX_OK && // Wait forever for smc_interrupted, limited for smc_busy replies.
-           (result->arg0 == kSmcInterrupted || (result->arg0 == kSmcBusy && busy_retry--))) {
-        if (result->arg0 == kSmcBusy) {
-            zx_nanosleep(zx_deadline_after(ZX_MSEC(busy_retry_msecs)));
-        }
-        params->arg6 = result->arg6; // Pass optional session_id received via x6 back in any retry.
-
-        zxlogf(TRACE, "SMC params 0x%X 0x%lX 0x%lX 0x%lX 0x%lX 0x%lX\n", params->func_id,
-               params->arg1, params->arg2, params->arg3, params->arg4, params->arg5);
-        status = zx_smc_call(smc_.get(), params, result);
-        zxlogf(TRACE, "SMC busy_retry %d results %ld 0x%lX 0x%lX 0x%lX\n",
-               busy_retries - busy_retry, result->arg0, result->arg1, result->arg2, result->arg3);
-    }
-    if (result->arg0 != 0) {
-        zxlogf(ERROR, "%s error %d\n", __func__, static_cast<int>(result->arg0));
-    }
-    return status;
-}
-
 zx_status_t PilDevice::Bind() {
     auto status = pdev_.GetSmc(0, &smc_);
     if (status != ZX_OK) {
@@ -255,7 +226,7 @@
         auto params = CreatePilSmcParams(PilCmd::QuerySupport,
                                          CreateSmcArgs(1, SmcArgType::kValue), i);
         zx_smc_result_t result = {};
-        status = SmcCall(&params, &result);
+        status = qcom::SmcCall(smc_.get(), &params, &result);
         if (status == ZX_OK && result.arg0 == kSmcOk && result.arg1 == 1) {
             zxlogf(INFO, "%s pas_id %d supported\n", __func__, i);
         }
diff --git a/zircon/system/dev/fw/qcom-pil/qcom-pil.h b/zircon/system/dev/fw/qcom-pil/qcom-pil.h
index 025bc17..04f8c13 100644
--- a/zircon/system/dev/fw/qcom-pil/qcom-pil.h
+++ b/zircon/system/dev/fw/qcom-pil/qcom-pil.h
@@ -101,10 +101,6 @@
 constexpr uint8_t kCallMask = 0xFF;
 constexpr uint8_t kCallShift = 0;
 
-constexpr uint64_t kSmcInterrupted = 1;
-constexpr uint64_t kSmcOk = 0;
-constexpr uint64_t kSmcBusy = -13;
-
 static constexpr uint32_t CreateFunctionId(CallType call_type,
                                            CallConvention call_conv,
                                            Service service,
@@ -171,7 +167,6 @@
     };
 
     void ShutDown();
-    zx_status_t SmcCall(zx_smc_parameters_t* params, zx_smc_result_t* result);
     int PilThread();
     zx_status_t LoadAuthFirmware(size_t fw_n);
 
diff --git a/zircon/system/dev/lib/qcom/BUILD.gn b/zircon/system/dev/lib/qcom/BUILD.gn
new file mode 100644
index 0000000..c7fa959
--- /dev/null
+++ b/zircon/system/dev/lib/qcom/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+library("qcom") {
+  sdk = "source"
+  sdk_headers = [ "qcom/smc.h" ]
+  sources = [
+    "qcom-smc.cpp",
+  ]
+  deps = [
+    "$zx/system/ulib/ddk",
+  ]
+}
diff --git a/zircon/system/dev/lib/qcom/include/qcom/smc.h b/zircon/system/dev/lib/qcom/include/qcom/smc.h
new file mode 100644
index 0000000..84bd559
--- /dev/null
+++ b/zircon/system/dev/lib/qcom/include/qcom/smc.h
@@ -0,0 +1,18 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <zircon/syscalls.h>
+#include <zircon/syscalls/smc.h>
+#include <zircon/types.h>
+
+namespace qcom {
+    constexpr uint64_t kSmcInterrupted = 1;
+    constexpr uint64_t kSmcOk = 0;
+    constexpr uint64_t kSmcBusy = -13;
+
+    zx_status_t SmcCall(zx_handle_t h, zx_smc_parameters_t* params, zx_smc_result_t* result);
+
+} // namespace qcom
diff --git a/zircon/system/dev/lib/qcom/qcom-smc.cpp b/zircon/system/dev/lib/qcom/qcom-smc.cpp
new file mode 100644
index 0000000..3fe3bc7
--- /dev/null
+++ b/zircon/system/dev/lib/qcom/qcom-smc.cpp
@@ -0,0 +1,40 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ddk/debug.h>
+#include <qcom/smc.h>
+
+namespace qcom {
+
+zx_status_t SmcCall(zx_handle_t h, zx_smc_parameters_t* params, zx_smc_result_t* result) {
+    zxlogf(TRACE, "SMC params 0x%X 0x%lX 0x%lX 0x%lX 0x%lX 0x%lX\n", params->func_id,
+           params->arg1, params->arg2, params->arg3, params->arg4, params->arg5);
+    auto status = zx_smc_call(h, params, result);
+    zxlogf(TRACE, "SMC results %ld 0x%lX 0x%lX 0x%lX\n", result->arg0, result->arg1,
+           result->arg2, result->arg3);
+
+    constexpr int total_retry_msecs = 2000;
+    constexpr int busy_retry_msecs = 30;
+    constexpr int busy_retries = total_retry_msecs / busy_retry_msecs;
+    int busy_retry = busy_retries;
+    while (status == ZX_OK && // Wait forever for smc_interrupted, limited for smc_busy replies.
+           (result->arg0 == kSmcInterrupted || (result->arg0 == kSmcBusy && busy_retry--))) {
+        if (result->arg0 == kSmcBusy) {
+            zx_nanosleep(zx_deadline_after(ZX_MSEC(busy_retry_msecs)));
+        }
+        params->arg6 = result->arg6; // Pass optional session_id received via x6 back in retry.
+
+        zxlogf(TRACE, "SMC params 0x%X 0x%lX 0x%lX 0x%lX 0x%lX 0x%lX\n", params->func_id,
+               params->arg1, params->arg2, params->arg3, params->arg4, params->arg5);
+        status = zx_smc_call(h, params, result);
+        zxlogf(TRACE, "SMC busy_retry %d results %ld 0x%lX 0x%lX 0x%lX\n",
+               busy_retries - busy_retry, result->arg0, result->arg1, result->arg2,
+               result->arg3);
+    }
+    if (result->arg0 != 0) {
+        zxlogf(ERROR, "%s error %d\n", __func__, static_cast<int>(result->arg0));
+    }
+    return status;
+}
+} // namespace qcom