blob: 031a0891799a68cb6118d2f840560f9ffb8dfe1e [file] [log] [blame]
// Copyright 2022 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 <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <fidl/fuchsia.hardware.qemuedu/cpp/wire.h>
#include <getopt.h>
#include <lib/fdio/directory.h>
#include <libgen.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
constexpr char kEduDevicePath[] =
"/dev/sys/platform/platform-passthrough/PCI0/bus/00:06.0_/qemu-edu";
constexpr char kEduDevicePath2[] =
"/dev/sys/platform/platform-passthrough/acpi/acpi-KBLT/qemu-edu";
int usage(const char* cmd) {
fprintf(stderr,
"\nInteract with the QEMU edu device:\n"
" %s live Performs a card liveness check\n"
" %s fact <n> Computes the factorial of n\n"
" %s help Print this message\n",
cmd, cmd, cmd);
return -1;
}
// Returns "true" if the argument matches the prefix.
// In this case, moves the argument past the prefix.
bool prefix_match(const char** arg, const char* prefix) {
if (!strncmp(*arg, prefix, strlen(prefix))) {
*arg += strlen(prefix);
return true;
}
return false;
}
fidl::WireSyncClient<fuchsia_hardware_qemuedu::Device> OpenDevice() {
int device = open(kEduDevicePath, O_RDWR);
if (device < 0) {
device = open(kEduDevicePath2, O_RDWR);
}
if (device < 0) {
fprintf(stderr, "Failed to open qemu edu device: %s\n", strerror(errno));
return {};
}
fidl::ClientEnd<fuchsia_hardware_qemuedu::Device> client_end;
zx_status_t st = fdio_get_service_handle(
device, client_end.channel().reset_and_get_address());
if (st != ZX_OK) {
fprintf(stderr, "Failed to get service handle: %s\n",
zx_status_get_string(st));
return {};
}
return fidl::BindSyncClient(std::move(client_end));
}
constexpr long kBadParse = -1;
long parse_positive_long(const char* number) {
char* end;
long result = strtol(number, &end, 10);
if (end == number || *end != '\0' || result < 0) {
return kBadParse;
}
return result;
}
int liveness_check() {
auto client = OpenDevice();
if (!client.is_valid()) {
return -1;
}
auto liveness_check_result = client->LivenessCheck();
if (!liveness_check_result.ok()) {
fprintf(stderr, "Error: failed to get liveness check result: %s\n",
zx_status_get_string(liveness_check_result.status()));
return -1;
}
if (liveness_check_result->result) {
printf("Liveness check passed!\n");
return 0;
} else {
printf("Liveness check failed!\n");
return -1;
}
}
// Use the qemu edu device to print the factorial of n to stdout.
// Returns 0 on success.
int compute_factorial(long n) {
auto client = OpenDevice();
if (!client.is_valid()) {
return -1;
}
if (n >= std::numeric_limits<uint32_t>::max()) {
fprintf(stderr, "N is too large\n");
return -1;
}
uint32_t input = static_cast<uint32_t>(n);
auto compute_factorial_result = client->ComputeFactorial(input);
if (!compute_factorial_result.ok()) {
fprintf(stderr, "Error: failed to call compute factorial result: %s\n",
zx_status_get_string(compute_factorial_result.status()));
return -1;
}
printf("Factorial(%u) = %u\n", input, compute_factorial_result->output);
return 0;
}
int main(int argc, char* argv[]) {
const char* cmd = basename(argv[0]);
// If no arguments passed, bail out after dumping
// usage information.
if (argc < 2) {
return usage(cmd);
}
const char* arg = argv[1];
if (prefix_match(&arg, "live")) {
return liveness_check();
} else if (prefix_match(&arg, "fact")) {
if (argc < 3) {
fprintf(stderr, "Expecting 1 argument\n");
return usage(cmd);
}
long n = parse_positive_long(argv[2]);
return compute_factorial(n);
}
return usage(cmd);
}