root/usr/src/test/os-tests/tests/stackalign/stackalign.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
 */

/*
 * Test that the stack is aligned to expected values.
 */

#include <stdio.h>
#include <pthread.h>
#include <thread.h>
#include <door.h>
#include <stdlib.h>
#include <unistd.h>
#include <ucontext.h>

#include <sys/stack.h>

/*
 * The introduction of SSE led to the IA32 ABI changing the required stack
 * alignment from 4 bytes to 16 bytes. Compilers assume this when using SSE.
 */
#if defined(__i386)
#undef STACK_ALIGN
#define STACK_ALIGN 16
#endif

#define ALIGN_ERR_IMPL(align, text)                             \
        "stack was not aligned to " #align " on " text "\n"
#define ALIGN_ERR_HELP(align, text) ALIGN_ERR_IMPL(align, text)
#define ALIGN_ERR(text) ALIGN_ERR_HELP(STACK_ALIGN, text)

#define STACK_SIZE 16*1024

typedef struct test_ctx {
        void (*func)(uintptr_t, char *);
        char *text;
} test_ctx_t;

extern void get_stack_at_entry(test_ctx_t *);

void
teststack(uintptr_t stack, char *arg)
{
        if ((stack & (STACK_ALIGN - 1)) != 0) {
                fprintf(stderr, ALIGN_ERR("%s"), (char *)arg);
                exit(1);
        }
}

void
initmain(uintptr_t stack)
{
        teststack(stack, "section .init");
}

void
initarray(uintptr_t stack)
{
        teststack(stack, "section .init_array");
}

void
doorstack(uintptr_t stack, char *arg)
{
        teststack(stack, arg);
        (void) door_return(NULL, 0, NULL, 0);
}

char door_arg[] = "DOOR ARG";

int
main(int argc, char *argv[])
{
        door_arg_t da = {
            .data_ptr = (void *)door_arg,
            .data_size = sizeof (door_arg)
        };
        test_ctx_t arg = {
            .func = teststack,
            .text = "pthread_create()"
        };
        ucontext_t back, uc;
        pthread_t tid;
        int door_fd, rc;

        if (pthread_create(&tid, NULL,
            (void *(*)(void *))(uintptr_t)get_stack_at_entry, &arg) != 0) {
                perror("pthread_create() failed:");
                exit(-2);
        }
        (void) pthread_join(tid, NULL);

        arg.text = "thr_create()";

        if (thr_create(NULL, 0,
            (void *(*)(void *))(uintptr_t)get_stack_at_entry,
            &arg, 0, &tid) != 0) {
                perror("thr_create() failed:");
                exit(-3);
        }
        (void) thr_join(tid, NULL, NULL);

        if (getcontext(&uc) < 0) {
                perror("getcontext() failed");
                exit(-4);
        }

        uc.uc_link = &back;
        uc.uc_stack.ss_size = STACK_SIZE;
        uc.uc_stack.ss_flags = 0;
        if ((uc.uc_stack.ss_sp = malloc(STACK_SIZE)) == NULL) {
                perror("failed to allocate stack");
                exit(-5);
        }

        arg.text = "swapcontext()";
        makecontext(&uc, (void (*)(void *))get_stack_at_entry, 1, &arg);
        if (swapcontext(&back, &uc) < 0) {
                perror("swapcontext() failed");
                exit(-6);
        }

        arg.func = doorstack;
        arg.text = "door_call()";

        if ((door_fd = door_create(
            (door_server_procedure_t *)(uintptr_t)get_stack_at_entry,
            &arg, 0)) < 0) {
                perror("failed to create door");
                exit(-7);
        }

        rc = door_call(door_fd, &da);

        if (rc < 0) {
                perror("door call #1 failed");
                exit(-8);
        }

        da.data_size += 5;
        rc = door_call(door_fd, &da);

        if (rc < 0) {
                perror("door call #2 failed");
                exit(-9);
        }

        (void) close(door_fd);

        return (0);
}