From 253e0383a38b689bd5170862dfec1e8ab83639f4 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Wed, 5 Feb 2020 19:40:46 -0500 Subject: [PATCH] tests/amdgpu: Secure bounce test (v4) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Acked-by: Leo Liu Signed-off-by: Luben Tuikov Acked-by: Christian König Signed-off-by: Alex Deucher --- tests/amdgpu/amdgpu_test.h | 8 + tests/amdgpu/basic_tests.c | 2 +- tests/amdgpu/security_tests.c | 359 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 360 insertions(+), 9 deletions(-) diff --git a/tests/amdgpu/amdgpu_test.h b/tests/amdgpu/amdgpu_test.h index ed7d954..98cec69 100644 --- a/tests/amdgpu/amdgpu_test.h +++ b/tests/amdgpu/amdgpu_test.h @@ -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_ */ diff --git a/tests/amdgpu/basic_tests.c b/tests/amdgpu/basic_tests.c index 34b9286..dc9ed94 100644 --- a/tests/amdgpu/basic_tests.c +++ b/tests/amdgpu/basic_tests.c @@ -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, diff --git a/tests/amdgpu/security_tests.c b/tests/amdgpu/security_tests.c index 87123ef..5c11ea0 100644 --- a/tests/amdgpu/security_tests.c +++ b/tests/amdgpu/security_tests.c @@ -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" @@ -27,10 +27,339 @@ #include "amdgpu_drm.h" #include "amdgpu_internal.h" +#include +#include +#include +#include +#include + 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; -- 2.7.4