1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
14  */
15 
16 /*
17  * Test that the stack is aligned to expected values.
18  */
19 
20 #include <stdio.h>
21 #include <pthread.h>
22 #include <thread.h>
23 #include <door.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <ucontext.h>
27 
28 #include <sys/stack.h>
29 
30 /*
31  * The introduction of SSE led to the IA32 ABI changing the required stack
32  * alignment from 4 bytes to 16 bytes. Compilers assume this when using SSE.
33  */
34 #if defined(__i386)
35 #undef STACK_ALIGN
36 #define	STACK_ALIGN 16
37 #endif
38 
39 #define	ALIGN_ERR_IMPL(align, text)				\
40 	"stack was not aligned to " #align " on " text "\n"
41 #define	ALIGN_ERR_HELP(align, text) ALIGN_ERR_IMPL(align, text)
42 #define	ALIGN_ERR(text) ALIGN_ERR_HELP(STACK_ALIGN, text)
43 
44 #define	STACK_SIZE 16*1024
45 
46 typedef struct test_ctx {
47 	void (*func)(uintptr_t, char *);
48 	char *text;
49 } test_ctx_t;
50 
51 extern void get_stack_at_entry(test_ctx_t *);
52 
53 void
54 teststack(uintptr_t stack, char *arg)
55 {
56 	if ((stack & (STACK_ALIGN - 1)) != 0) {
57 		fprintf(stderr, ALIGN_ERR("%s"), (char *)arg);
58 		exit(1);
59 	}
60 }
61 
62 void
63 initmain(uintptr_t stack)
64 {
65 	teststack(stack, "section .init");
66 }
67 
68 void
69 initarray(uintptr_t stack)
70 {
71 	teststack(stack, "section .init_array");
72 }
73 
74 void
75 doorstack(uintptr_t stack, char *arg)
76 {
77 	teststack(stack, arg);
78 	(void) door_return(NULL, 0, NULL, 0);
79 }
80 
81 char door_arg[] = "DOOR ARG";
82 
83 int
84 main(int argc, char *argv[])
85 {
86 	door_arg_t da = {
87 	    .data_ptr = (void *)door_arg,
88 	    .data_size = sizeof (door_arg)
89 	};
90 	test_ctx_t arg = {
91 	    .func = teststack,
92 	    .text = "pthread_create()"
93 	};
94 	ucontext_t back, uc;
95 	pthread_t tid;
96 	int door_fd, rc;
97 
98 #if defined(__sparc)
99 	/*
100 	 * This hasn't been implemented for SPARC, so skip.
101 	 */
102 	fprintf(stderr, "No SPARC implementation of get_stack_at_entry\n");
103 	return (3);
104 #else
105 	if (pthread_create(&tid, NULL,
106 	    (void *(*)(void *))get_stack_at_entry, &arg) != 0) {
107 		perror("pthread_create() failed:");
108 		exit(-2);
109 	}
110 	(void) pthread_join(tid, NULL);
111 
112 	arg.text = "thr_create()";
113 
114 	if (thr_create(NULL, 0, (void *(*)(void *))get_stack_at_entry,
115 	    &arg, 0, &tid) != 0) {
116 		perror("thr_create() failed:");
117 		exit(-3);
118 	}
119 	(void) thr_join(tid, NULL, NULL);
120 
121 	if (getcontext(&uc) < 0) {
122 		perror("getcontext() failed");
123 		exit(-4);
124 	}
125 
126 	uc.uc_link = &back;
127 	uc.uc_stack.ss_size = STACK_SIZE;
128 	uc.uc_stack.ss_flags = 0;
129 	if ((uc.uc_stack.ss_sp = malloc(STACK_SIZE)) == NULL) {
130 		perror("failed to allocate stack");
131 		exit(-5);
132 	}
133 
134 	arg.text = "swapcontext()";
135 	makecontext(&uc, (void (*)(void *))get_stack_at_entry, 1, &arg);
136 	if (swapcontext(&back, &uc) < 0) {
137 		perror("swapcontext() failed");
138 		exit(-6);
139 	}
140 
141 	arg.func = doorstack;
142 	arg.text = "door_call()";
143 
144 	if ((door_fd = door_create(
145 	    (door_server_procedure_t *)get_stack_at_entry,
146 	    &arg, 0)) < 0) {
147 		perror("failed to create door");
148 		exit(-7);
149 	}
150 
151 	rc = door_call(door_fd, &da);
152 
153 	if (rc < 0) {
154 		perror("door call #1 failed");
155 		exit(-8);
156 	}
157 
158 	da.data_size += 5;
159 	rc = door_call(door_fd, &da);
160 
161 	if (rc < 0) {
162 		perror("door call #2 failed");
163 		exit(-9);
164 	}
165 
166 	(void) close(door_fd);
167 
168 	return (0);
169 #endif
170 }
171