Path: blob/master/tools/testing/selftests/arm64/fp/kernel-test.c
26288 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2024 ARM Limited.3*/45#define _GNU_SOURCE67#include <stdio.h>8#include <stdlib.h>9#include <stdbool.h>10#include <errno.h>11#include <fcntl.h>12#include <signal.h>13#include <string.h>14#include <unistd.h>1516#include <sys/socket.h>1718#include <linux/kernel.h>19#include <linux/if_alg.h>2021#define DATA_SIZE (16 * 4096)2223static int base, sock;2425static int digest_len;26static char *ref;27static char *digest;28static char *alg_name;2930static struct iovec data_iov;31static int zerocopy[2];32static int sigs;33static int iter;3435static void handle_exit_signal(int sig, siginfo_t *info, void *context)36{37printf("Terminated by signal %d, iterations=%d, signals=%d\n",38sig, iter, sigs);39exit(0);40}4142static void handle_kick_signal(int sig, siginfo_t *info, void *context)43{44sigs++;45}4647static char *drivers[] = {48"sha1-ce",49"sha224-arm64",50"sha224-arm64-neon",51"sha224-ce",52"sha256-arm64",53"sha256-arm64-neon",54"sha256-ce",55"sha384-ce",56"sha512-ce",57"sha3-224-ce",58"sha3-256-ce",59"sha3-384-ce",60"sha3-512-ce",61"sm3-ce",62"sm3-neon",63};6465static bool create_socket(void)66{67FILE *proc;68struct sockaddr_alg addr;69char buf[1024];70char *c, *driver_name;71bool is_shash, match;72int ret, i;7374ret = socket(AF_ALG, SOCK_SEQPACKET, 0);75if (ret < 0) {76if (errno == EAFNOSUPPORT) {77printf("AF_ALG not supported\n");78return false;79}8081printf("Failed to create AF_ALG socket: %s (%d)\n",82strerror(errno), errno);83return false;84}85base = ret;8687memset(&addr, 0, sizeof(addr));88addr.salg_family = AF_ALG;89strncpy((char *)addr.salg_type, "hash", sizeof(addr.salg_type));9091proc = fopen("/proc/crypto", "r");92if (!proc) {93printf("Unable to open /proc/crypto\n");94return false;95}9697driver_name = NULL;98is_shash = false;99match = false;100101/* Look through /proc/crypto for a driver with kernel mode FP usage */102while (!match) {103c = fgets(buf, sizeof(buf), proc);104if (!c) {105if (feof(proc)) {106printf("Nothing found in /proc/crypto\n");107return false;108}109continue;110}111112/* Algorithm descriptions are separated by a blank line */113if (*c == '\n') {114if (is_shash && driver_name) {115for (i = 0; i < ARRAY_SIZE(drivers); i++) {116if (strcmp(drivers[i],117driver_name) == 0) {118match = true;119}120}121}122123if (!match) {124digest_len = 0;125126free(driver_name);127driver_name = NULL;128129free(alg_name);130alg_name = NULL;131132is_shash = false;133}134continue;135}136137/* Remove trailing newline */138c = strchr(buf, '\n');139if (c)140*c = '\0';141142/* Find the field/value separator and start of the value */143c = strchr(buf, ':');144if (!c)145continue;146c += 2;147148if (strncmp(buf, "digestsize", strlen("digestsize")) == 0)149sscanf(c, "%d", &digest_len);150151if (strncmp(buf, "name", strlen("name")) == 0)152alg_name = strdup(c);153154if (strncmp(buf, "driver", strlen("driver")) == 0)155driver_name = strdup(c);156157if (strncmp(buf, "type", strlen("type")) == 0)158if (strncmp(c, "shash", strlen("shash")) == 0)159is_shash = true;160}161162strncpy((char *)addr.salg_name, alg_name,163sizeof(addr.salg_name) - 1);164165ret = bind(base, (struct sockaddr *)&addr, sizeof(addr));166if (ret < 0) {167printf("Failed to bind %s: %s (%d)\n",168addr.salg_name, strerror(errno), errno);169return false;170}171172ret = accept(base, NULL, 0);173if (ret < 0) {174printf("Failed to accept %s: %s (%d)\n",175addr.salg_name, strerror(errno), errno);176return false;177}178179sock = ret;180181ret = pipe(zerocopy);182if (ret != 0) {183printf("Failed to create zerocopy pipe: %s (%d)\n",184strerror(errno), errno);185return false;186}187188ref = malloc(digest_len);189if (!ref) {190printf("Failed to allocated %d byte reference\n", digest_len);191return false;192}193194digest = malloc(digest_len);195if (!digest) {196printf("Failed to allocated %d byte digest\n", digest_len);197return false;198}199200return true;201}202203static bool compute_digest(void *buf)204{205struct iovec iov;206int ret, wrote;207208iov = data_iov;209while (iov.iov_len) {210ret = vmsplice(zerocopy[1], &iov, 1, SPLICE_F_GIFT);211if (ret < 0) {212printf("Failed to send buffer: %s (%d)\n",213strerror(errno), errno);214return false;215}216217wrote = ret;218ret = splice(zerocopy[0], NULL, sock, NULL, wrote, 0);219if (ret < 0) {220printf("Failed to splice buffer: %s (%d)\n",221strerror(errno), errno);222} else if (ret != wrote) {223printf("Short splice: %d < %d\n", ret, wrote);224}225226iov.iov_len -= wrote;227iov.iov_base += wrote;228}229230reread:231ret = recv(sock, buf, digest_len, 0);232if (ret == 0) {233printf("No digest returned\n");234return false;235}236if (ret != digest_len) {237if (errno == -EAGAIN)238goto reread;239printf("Failed to get digest: %s (%d)\n",240strerror(errno), errno);241return false;242}243244return true;245}246247int main(void)248{249char *data;250struct sigaction sa;251int ret;252253/* Ensure we have unbuffered output */254setvbuf(stdout, NULL, _IOLBF, 0);255256/* The parent will communicate with us via signals */257memset(&sa, 0, sizeof(sa));258sa.sa_sigaction = handle_exit_signal;259sa.sa_flags = SA_RESTART | SA_SIGINFO;260sigemptyset(&sa.sa_mask);261ret = sigaction(SIGTERM, &sa, NULL);262if (ret < 0)263printf("Failed to install SIGTERM handler: %s (%d)\n",264strerror(errno), errno);265266sa.sa_sigaction = handle_kick_signal;267ret = sigaction(SIGUSR1, &sa, NULL);268if (ret < 0)269printf("Failed to install SIGUSR1 handler: %s (%d)\n",270strerror(errno), errno);271ret = sigaction(SIGUSR2, &sa, NULL);272if (ret < 0)273printf("Failed to install SIGUSR2 handler: %s (%d)\n",274strerror(errno), errno);275276data = malloc(DATA_SIZE);277if (!data) {278printf("Failed to allocate data buffer\n");279return EXIT_FAILURE;280}281memset(data, 0, DATA_SIZE);282283data_iov.iov_base = data;284data_iov.iov_len = DATA_SIZE;285286/*287* If we can't create a socket assume it's a lack of system288* support and fall back to a basic FPSIMD test for the289* benefit of fp-stress.290*/291if (!create_socket()) {292execl("./fpsimd-test", "./fpsimd-test", NULL);293printf("Failed to fall back to fspimd-test: %d (%s)\n",294errno, strerror(errno));295return EXIT_FAILURE;296}297298/*299* Compute a reference digest we hope is repeatable, we do300* this at runtime partly to make it easier to play with301* parameters.302*/303if (!compute_digest(ref)) {304printf("Failed to compute reference digest\n");305return EXIT_FAILURE;306}307308printf("AF_ALG using %s\n", alg_name);309310while (true) {311if (!compute_digest(digest)) {312printf("Failed to compute digest, iter=%d\n", iter);313return EXIT_FAILURE;314}315316if (memcmp(ref, digest, digest_len) != 0) {317printf("Digest mismatch, iter=%d\n", iter);318return EXIT_FAILURE;319}320321iter++;322}323324return EXIT_FAILURE;325}326327328