1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <autoconf.h>
14#include <utils/gen_config.h>
15#include <utils/stack.h>
16#include <utils/arith.h>
17#include <utils/util.h>
18
19void *
20utils_run_on_stack(void *stack_top, void * (*func)(void *arg), void *arg)
21{
22    if (stack_top == NULL) {
23        ZF_LOGE("Invalid stack address \n");
24        return NULL;
25    } else if (!IS_ALIGNED((uintptr_t) stack_top, 4)) {
26      /* Stack has to be aligned to 16-bytes */
27        ZF_LOGE("Invalid stack alignment. Stack has to be 16-bytes aligned \n");
28        return NULL;
29    }
30
31/*
32 * Note that x86-64 ABI requires that at least SSE and
33 * SSE2 are supported, and thus the API uses xmm registers
34 * for passing floating point parameters. The GCC compiler
35 * may use the movaps instruction to store the context
36 * xmm regsiters on stack, and these store operations
37 * require that the stack addresses are 16-byte
38 * aligned, so we push oen more dummy number to the stack
39 * for alignment purpose.
40 */
41#ifdef CONFIG_ARCH_X86_64
42    void *ret;
43    asm volatile (
44        "movq   %%rsp, %%rcx\n\t"
45        "movq   %[new_stack], %%rsp\n\t"
46        "push   %%rcx\n\t"
47        "pushq  $0\n\t"             /* dummy number for stack alignment */
48        "movq   %[arg], %%rdi\n\t"
49        "call   *%[func]\n\t"
50        "add    $0x8, %%rsp\n\t"
51        "pop    %%rsp\n\t"
52        : [ret] "=a" (ret)
53        : [new_stack] "r" (stack_top),
54        [func] "r" (func),
55        [arg] "r" (arg)
56        : "rcx", "rbx", "rdi"
57    );
58#else
59    void *ret;
60    asm volatile (
61        "mov %%esp, %%ecx\n\t"          /* Save sp. */
62        "mov %[new_stack], %%esp\n\t"   /* Switch to new stack. */
63        "pushl %%ecx\n\t"               /* Push old sp onto new stack. */
64        "subl $8, %%esp\n\t"            /* dummy padding for 16-byte stack alignment */
65        "pushl %[arg]\n\t"              /* Setup argument to func. */
66        "call *%[func]\n\t"
67        "add $12, %%esp\n\t"
68        "popl %%esp\n\t"                /* Switch back! */
69        : [ret] "=a" (ret)
70        : [new_stack] "r" (stack_top),
71        [func] "r" (func),
72        [arg] "r" (arg)
73        : "ecx", "ebx");
74#endif /* CONFIG_ARCH_X86_64 */
75    return ret;
76}
77