root/tools/testing/selftests/powerpc/pmu/lib.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2014, Michael Ellerman, IBM Corp.
 */

#define _GNU_SOURCE     /* For CPU_ZERO etc. */

#include <errno.h>
#include <sched.h>
#include <setjmp.h>
#include <stdlib.h>
#include <sys/wait.h>

#include "utils.h"
#include "lib.h"

#define PARENT_TOKEN    0xAA
#define CHILD_TOKEN     0x55

int sync_with_child(union pipe read_pipe, union pipe write_pipe)
{
        char c = PARENT_TOKEN;

        FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
        FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
        if (c != CHILD_TOKEN) /* sometimes expected */
                return 1;

        return 0;
}

int wait_for_parent(union pipe read_pipe)
{
        char c;

        FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
        FAIL_IF(c != PARENT_TOKEN);

        return 0;
}

int notify_parent(union pipe write_pipe)
{
        char c = CHILD_TOKEN;

        FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);

        return 0;
}

int notify_parent_of_error(union pipe write_pipe)
{
        char c = ~CHILD_TOKEN;

        FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);

        return 0;
}

int wait_for_child(pid_t child_pid)
{
        int rc;

        if (waitpid(child_pid, &rc, 0) == -1) {
                perror("waitpid");
                return 1;
        }

        if (WIFEXITED(rc))
                rc = WEXITSTATUS(rc);
        else
                rc = 1; /* Signal or other */

        return rc;
}

int kill_child_and_wait(pid_t child_pid)
{
        kill(child_pid, SIGTERM);

        return wait_for_child(child_pid);
}

static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
{
        volatile int i = 0;

        /*
         * We are just here to eat cpu and die. So make sure we can be killed,
         * and also don't do any custom SIGTERM handling.
         */
        signal(SIGTERM, SIG_DFL);

        notify_parent(write_pipe);
        wait_for_parent(read_pipe);

        /* Soak up cpu forever */
        while (1) i++;

        return 0;
}

pid_t eat_cpu(int (test_function)(void))
{
        union pipe read_pipe, write_pipe;
        int rc;
        pid_t pid;

        FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);

        if (pipe(read_pipe.fds) == -1)
                return -1;

        if (pipe(write_pipe.fds) == -1)
                return -1;

        pid = fork();
        if (pid == 0)
                exit(eat_cpu_child(write_pipe, read_pipe));

        if (sync_with_child(read_pipe, write_pipe)) {
                rc = -1;
                goto out;
        }

        printf("main test running as pid %d\n", getpid());

        rc = test_function();
out:
        kill(pid, SIGKILL);

        return rc;
}

struct addr_range libc, vdso;

int parse_proc_maps(void)
{
        unsigned long start, end;
        char execute, name[128];
        FILE *f;
        int rc;

        f = fopen("/proc/self/maps", "r");
        if (!f) {
                perror("fopen");
                return -1;
        }

        do {
                /* This skips line with no executable which is what we want */
                rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
                            &start, &end, &execute, name);
                if (rc <= 0)
                        break;

                if (execute != 'x')
                        continue;

                if (strstr(name, "libc")) {
                        libc.first = start;
                        libc.last = end - 1;
                } else if (strstr(name, "[vdso]")) {
                        vdso.first = start;
                        vdso.last = end - 1;
                }
        } while(1);

        fclose(f);

        return 0;
}

#define PARANOID_PATH   "/proc/sys/kernel/perf_event_paranoid"

bool require_paranoia_below(int level)
{
        int err;
        long current;

        err = read_long(PARANOID_PATH, &current, 10);
        if (err) {
                printf("Couldn't parse " PARANOID_PATH "?\n");
                return false;
        }

        return current < level;
}