tests/amdgpu: Secure bounce test (v4)
authorLuben Tuikov <luben.tuikov@amd.com>
Thu, 6 Feb 2020 00:40:46 +0000 (19:40 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 15 Oct 2020 17:47:00 +0000 (13:47 -0400)
Implement secure bounce test. Steps implemented
as outlined by Christian K.

v2: Remove gpu_info; add comment describing
    the purpose and steps of the test.
v3: Parameterize "secure" in amdgpu_bo_lcopy() and
    amdgpu_bo_move(). Set them both to 0.
    Allocate buffer Bob to be non-TMZ.
v4: Fix an off-by-one bug which was causing
    the test to segfault.

Acked-by: Huang Rui <ray.huang@amd.com>
Acked-by: Leo Liu <leo.liu@amd.com>
Signed-off-by: Luben Tuikov <luben.tuikov@amd.com>
Acked-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
tests/amdgpu/amdgpu_test.h
tests/amdgpu/basic_tests.c
tests/amdgpu/security_tests.c

index ed7d954..98cec69 100644 (file)
@@ -462,5 +462,13 @@ static inline bool asic_is_arcturus(uint32_t asic_id)
        }
 }
 
+void amdgpu_test_exec_cs_helper_raw(amdgpu_device_handle device_handle,
+                                   amdgpu_context_handle context_handle,
+                                   unsigned ip_type, int instance, int pm4_dw,
+                                   uint32_t *pm4_src, int res_cnt,
+                                   amdgpu_bo_handle *resources,
+                                   struct amdgpu_cs_ib_info *ib_info,
+                                   struct amdgpu_cs_request *ibs_request,
+                                   bool secure);
 
 #endif  /* #ifdef _AMDGPU_TEST_H_ */
index 34b9286..dc9ed94 100644 (file)
@@ -1298,7 +1298,7 @@ static void amdgpu_command_submission_compute(void)
  * pm4_src, resources, ib_info, and ibs_request
  * submit command stream described in ibs_request and wait for this IB accomplished
  */
-static void
+void
 amdgpu_test_exec_cs_helper_raw(amdgpu_device_handle device_handle,
                               amdgpu_context_handle context_handle,
                               unsigned ip_type, int instance, int pm4_dw,
index 87123ef..5c11ea0 100644 (file)
@@ -19,7 +19,7 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  *
-*/
+ */
 
 #include "CUnit/Basic.h"
 
 #include "amdgpu_drm.h"
 #include "amdgpu_internal.h"
 
+#include <string.h>
+#include <unistd.h>
+#include <endian.h>
+#include <strings.h>
+#include <xf86drm.h>
+
 static amdgpu_device_handle device_handle;
 static uint32_t major_version;
 static uint32_t minor_version;
 
+static struct drm_amdgpu_info_hw_ip  sdma_info;
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(_Arr)  (sizeof(_Arr)/sizeof((_Arr)[0]))
+#endif
+
+
+/* --------------------- Secure bounce test ------------------------ *
+ *
+ * The secure bounce test tests that we can evict a TMZ buffer,
+ * and page it back in, via a bounce buffer, as it encryption/decryption
+ * depends on its physical address, and have the same data, i.e. data
+ * integrity is preserved.
+ *
+ * The steps are as follows (from Christian K.):
+ *
+ * Buffer A which is TMZ protected and filled by the CPU with a
+ * certain pattern. That the GPU is reading only random nonsense from
+ * that pattern is irrelevant for the test.
+ *
+ * This buffer A is then secure copied into buffer B which is also
+ * TMZ protected.
+ *
+ * Buffer B is moved around, from VRAM to GTT, GTT to SYSTEM,
+ * etc.
+ *
+ * Then, we use another secure copy of buffer B back to buffer A.
+ *
+ * And lastly we check with the CPU the pattern.
+ *
+ * Assuming that we don't have memory contention and buffer A stayed
+ * at the same place, we should still see the same pattern when read
+ * by the CPU.
+ *
+ * If we don't see the same pattern then something in the buffer
+ * migration code is not working as expected.
+ */
+
+#define SECURE_BOUNCE_TEST_STR    "secure bounce"
+#define SECURE_BOUNCE_FAILED_STR  SECURE_BOUNCE_TEST_STR " failed"
+
+#define PRINT_ERROR(_Res)   fprintf(stderr, "%s:%d: %s (%d)\n",        \
+                                   __func__, __LINE__, strerror(-(_Res)), _Res)
+
+#define PACKET_LCOPY_SIZE         7
+#define PACKET_NOP_SIZE          12
+
+struct sec_amdgpu_bo {
+       struct amdgpu_bo *bo;
+       struct amdgpu_va *va;
+};
+
+struct command_ctx {
+       struct amdgpu_device    *dev;
+       struct amdgpu_cs_ib_info cs_ibinfo;
+       struct amdgpu_cs_request cs_req;
+       struct amdgpu_context   *context;
+       int ring_id;
+};
+
+/**
+ * amdgpu_bo_alloc_map -- Allocate and map a buffer object (BO)
+ * @dev: The AMDGPU device this BO belongs to.
+ * @size: The size of the BO.
+ * @alignment: Alignment of the BO.
+ * @gem_domain: One of AMDGPU_GEM_DOMAIN_xyz.
+ * @alloc_flags: One of AMDGPU_GEM_CREATE_xyz.
+ * @sbo: the result
+ *
+ * Allocate a buffer object (BO) with the desired attributes
+ * as specified by the argument list and write out the result
+ * into @sbo.
+ *
+ * Return 0 on success and @sbo->bo and @sbo->va are set,
+ * or -errno on error.
+ */
+static int amdgpu_bo_alloc_map(struct amdgpu_device *dev,
+                              unsigned size,
+                              unsigned alignment,
+                              unsigned gem_domain,
+                              uint64_t alloc_flags,
+                              struct sec_amdgpu_bo *sbo)
+{
+       void *cpu;
+       uint64_t mc_addr;
+
+       return amdgpu_bo_alloc_and_map_raw(dev,
+                                          size,
+                                          alignment,
+                                          gem_domain,
+                                          alloc_flags,
+                                          0,
+                                          &sbo->bo,
+                                          &cpu, &mc_addr,
+                                          &sbo->va);
+}
+
+static void amdgpu_bo_unmap_free(struct sec_amdgpu_bo *sbo,
+                                const uint64_t size)
+{
+       (void) amdgpu_bo_unmap_and_free(sbo->bo,
+                                       sbo->va,
+                                       sbo->va->address,
+                                       size);
+       sbo->bo = NULL;
+       sbo->va = NULL;
+}
+
+static void amdgpu_sdma_lcopy(uint32_t *packet,
+                             const uint64_t dst,
+                             const uint64_t src,
+                             const uint32_t size,
+                             const int secure)
+{
+       /* Set the packet to Linear copy with TMZ set.
+        */
+       packet[0] = htole32(secure << 18 | 1);
+       packet[1] = htole32(size-1);
+       packet[2] = htole32(0);
+       packet[3] = htole32((uint32_t)(src & 0xFFFFFFFFU));
+       packet[4] = htole32((uint32_t)(src >> 32));
+       packet[5] = htole32((uint32_t)(dst & 0xFFFFFFFFU));
+       packet[6] = htole32((uint32_t)(dst >> 32));
+}
+
+static void amdgpu_sdma_nop(uint32_t *packet, uint32_t nop_count)
+{
+       /* A packet of the desired number of NOPs.
+        */
+       packet[0] = htole32(nop_count << 16);
+       for ( ; nop_count > 0; nop_count--)
+               packet[nop_count-1] = 0;
+}
+
+/**
+ * amdgpu_bo_lcopy -- linear copy with TZM set, using sDMA
+ * @dev: AMDGPU device to which both buffer objects belong to
+ * @dst: destination buffer object
+ * @src: source buffer object
+ * @size: size of memory to move, in bytes.
+ * @secure: Set to 1 to perform secure copy, 0 for clear
+ *
+ * Issues and waits for completion of a Linear Copy with TMZ
+ * set, to the sDMA engine. @size should be a multiple of
+ * at least 16 bytes.
+ */
+static void amdgpu_bo_lcopy(struct command_ctx *ctx,
+                           struct sec_amdgpu_bo *dst,
+                           struct sec_amdgpu_bo *src,
+                           const uint32_t size,
+                           int secure)
+{
+       struct amdgpu_bo *bos[] = { dst->bo, src->bo };
+       uint32_t packet[PACKET_LCOPY_SIZE];
+
+       amdgpu_sdma_lcopy(packet,
+                         dst->va->address,
+                         src->va->address,
+                         size, secure);
+       amdgpu_test_exec_cs_helper_raw(ctx->dev, ctx->context,
+                                      AMDGPU_HW_IP_DMA, ctx->ring_id,
+                                      ARRAY_SIZE(packet), packet,
+                                      ARRAY_SIZE(bos), bos,
+                                      &ctx->cs_ibinfo, &ctx->cs_req,
+                                      secure == 1);
+}
+
+/**
+ * amdgpu_bo_move -- Evoke a move of the buffer object (BO)
+ * @dev: device to which this buffer object belongs to
+ * @bo: the buffer object to be moved
+ * @whereto: one of AMDGPU_GEM_DOMAIN_xyz
+ * @secure: set to 1 to submit secure IBs
+ *
+ * Evokes a move of the buffer object @bo to the GEM domain
+ * descibed by @whereto.
+ *
+ * Returns 0 on sucess; -errno on error.
+ */
+static int amdgpu_bo_move(struct command_ctx *ctx,
+                         struct amdgpu_bo *bo,
+                         uint64_t whereto,
+                         int secure)
+{
+       struct amdgpu_bo *bos[] = { bo };
+       struct drm_amdgpu_gem_op gop = {
+               .handle  = bo->handle,
+               .op      = AMDGPU_GEM_OP_SET_PLACEMENT,
+               .value   = whereto,
+       };
+       uint32_t packet[PACKET_NOP_SIZE];
+       int res;
+
+       /* Change the buffer's placement.
+        */
+       res = drmIoctl(ctx->dev->fd, DRM_IOCTL_AMDGPU_GEM_OP, &gop);
+       if (res)
+               return -errno;
+
+       /* Now issue a NOP to actually evoke the MM to move
+        * it to the desired location.
+        */
+       amdgpu_sdma_nop(packet, PACKET_NOP_SIZE);
+       amdgpu_test_exec_cs_helper_raw(ctx->dev, ctx->context,
+                                      AMDGPU_HW_IP_DMA, ctx->ring_id,
+                                      ARRAY_SIZE(packet), packet,
+                                      ARRAY_SIZE(bos), bos,
+                                      &ctx->cs_ibinfo, &ctx->cs_req,
+                                      secure == 1);
+       return 0;
+}
+
+/* Safe, O Sec!
+ */
+static const uint8_t secure_pattern[] = { 0x5A, 0xFE, 0x05, 0xEC };
+
+#define SECURE_BUFFER_SIZE       (4 * 1024 * sizeof(secure_pattern))
+
+static void amdgpu_secure_bounce(void)
+{
+       struct sec_amdgpu_bo alice, bob;
+       struct command_ctx   sb_ctx;
+       long page_size;
+       uint8_t *pp;
+       int res;
+
+       page_size = sysconf(_SC_PAGESIZE);
+
+       memset(&sb_ctx, 0, sizeof(sb_ctx));
+       sb_ctx.dev = device_handle;
+       res = amdgpu_cs_ctx_create(sb_ctx.dev, &sb_ctx.context);
+       if (res) {
+               PRINT_ERROR(res);
+               CU_FAIL(SECURE_BOUNCE_FAILED_STR);
+               return;
+       }
+
+       /* Use the first present ring.
+        */
+       res = ffs(sdma_info.available_rings) - 1;
+       if (res == -1) {
+               PRINT_ERROR(-ENOENT);
+               CU_FAIL(SECURE_BOUNCE_FAILED_STR);
+               goto Out_free_ctx;
+       }
+       sb_ctx.ring_id = res;
+
+       /* Allocate a buffer named Alice in VRAM.
+        */
+       res = amdgpu_bo_alloc_map(device_handle,
+                                 SECURE_BUFFER_SIZE,
+                                 page_size,
+                                 AMDGPU_GEM_DOMAIN_VRAM,
+                                 AMDGPU_GEM_CREATE_ENCRYPTED,
+                                 &alice);
+       if (res) {
+               PRINT_ERROR(res);
+               CU_FAIL(SECURE_BOUNCE_FAILED_STR);
+               return;
+       }
+
+       /* Fill Alice with a pattern.
+        */
+       for (pp = alice.bo->cpu_ptr;
+            pp < (typeof(pp)) alice.bo->cpu_ptr + SECURE_BUFFER_SIZE;
+            pp += sizeof(secure_pattern))
+               memcpy(pp, secure_pattern, sizeof(secure_pattern));
+
+       /* Allocate a buffer named Bob in VRAM.
+        */
+       res = amdgpu_bo_alloc_map(device_handle,
+                                 SECURE_BUFFER_SIZE,
+                                 page_size,
+                                 AMDGPU_GEM_DOMAIN_VRAM,
+                                 0 /* AMDGPU_GEM_CREATE_ENCRYPTED */,
+                                 &bob);
+       if (res) {
+               PRINT_ERROR(res);
+               CU_FAIL(SECURE_BOUNCE_FAILED_STR);
+               goto Out_free_Alice;
+       }
+
+       /* sDMA clear copy from Alice to Bob.
+        */
+       amdgpu_bo_lcopy(&sb_ctx, &bob, &alice, SECURE_BUFFER_SIZE, 0);
+
+       /* Move Bob to the GTT domain.
+        */
+       res = amdgpu_bo_move(&sb_ctx, bob.bo, AMDGPU_GEM_DOMAIN_GTT, 0);
+       if (res) {
+               PRINT_ERROR(res);
+               CU_FAIL(SECURE_BOUNCE_FAILED_STR);
+               goto Out_free_all;
+       }
+
+       /* sDMA clear copy from Bob to Alice.
+        */
+       amdgpu_bo_lcopy(&sb_ctx, &alice, &bob, SECURE_BUFFER_SIZE, 0);
+
+       /* Verify the contents of Alice.
+        */
+       for (pp = alice.bo->cpu_ptr;
+            pp < (typeof(pp)) alice.bo->cpu_ptr + SECURE_BUFFER_SIZE;
+            pp += sizeof(secure_pattern)) {
+               res = memcmp(pp, secure_pattern, sizeof(secure_pattern));
+               if (res) {
+                       fprintf(stderr, SECURE_BOUNCE_FAILED_STR);
+                       CU_FAIL(SECURE_BOUNCE_FAILED_STR);
+                       break;
+               }
+       }
+
+Out_free_all:
+       amdgpu_bo_unmap_free(&bob, SECURE_BUFFER_SIZE);
+Out_free_Alice:
+       amdgpu_bo_unmap_free(&alice, SECURE_BUFFER_SIZE);
+Out_free_ctx:
+       res = amdgpu_cs_ctx_free(sb_ctx.context);
+       CU_ASSERT_EQUAL(res, 0);
+}
+
+/* ----------------------------------------------------------------- */
+
 static void amdgpu_security_alloc_buf_test(void)
 {
        amdgpu_bo_handle bo;
@@ -84,9 +413,10 @@ static void amdgpu_security_sdma_submission_test(void)
 /* ----------------------------------------------------------------- */
 
 CU_TestInfo security_tests[] = {
-       { "allocate secure buffer test", amdgpu_security_alloc_buf_test },
+       { "allocate secure buffer test",        amdgpu_security_alloc_buf_test },
        { "graphics secure command submission", amdgpu_security_gfx_submission_test },
-       { "sdma secure command submission", amdgpu_security_sdma_submission_test },
+       { "sDMA secure command submission",     amdgpu_security_sdma_submission_test },
+       { SECURE_BOUNCE_TEST_STR,               amdgpu_secure_bounce },
        CU_TEST_INFO_NULL,
 };
 
@@ -119,19 +449,32 @@ CU_BOOL suite_security_tests_enable(void)
 
 int suite_security_tests_init(void)
 {
-       if (amdgpu_device_initialize(drm_amdgpu[0], &major_version,
-                                    &minor_version, &device_handle))
+       int res;
+
+       res = amdgpu_device_initialize(drm_amdgpu[0], &major_version,
+                                      &minor_version, &device_handle);
+       if (res) {
+               PRINT_ERROR(res);
                return CUE_SINIT_FAILED;
+       }
+
+       res = amdgpu_query_hw_ip_info(device_handle,
+                                     AMDGPU_HW_IP_DMA,
+                                     0, &sdma_info);
+       if (res) {
+               PRINT_ERROR(res);
+               return CUE_SINIT_FAILED;
+       }
 
        return CUE_SUCCESS;
 }
 
 int suite_security_tests_clean(void)
 {
-       int r;
+       int res;
 
-       r = amdgpu_device_deinitialize(device_handle);
-       if (r)
+       res = amdgpu_device_deinitialize(device_handle);
+       if (res)
                return CUE_SCLEAN_FAILED;
 
        return CUE_SUCCESS;