1 /*
2 * Copyright (c) 2024, Tenstorrent AI ULC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <sys/mman.h>
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/net/socket.h>
11 #include <zephyr/posix/fcntl.h>
12 #include <zephyr/sys/fdtable.h>
13
14 #include <zephyr/ztest.h>
15
16 #define SHM_SIZE 8
17
18 #define VALID_SHM_PATH "/foo"
19 #define INVALID_SHM_PATH "foo"
20 #define EMPTY_SHM_PATH ""
21 #define TOO_SHORT_SHM_PATH "/"
22
23 #define INVALID_MODE 0
24 #define VALID_MODE 0666
25
26 #define INVALID_FLAGS 0
27 #define VALID_FLAGS (O_RDWR | O_CREAT)
28 #define CREATE_FLAGS VALID_FLAGS
29 #define OPEN_FLAGS (VALID_FLAGS & ~O_CREAT)
30
31 /* account for stdin, stdout, stderr */
32 #define N (CONFIG_ZVFS_OPEN_MAX - 3)
33
34 /* we need to have at least 2 shared memory objects */
35 BUILD_ASSERT(N >= 2, "CONFIG_ZVFS_OPEN_MAX must be > 4");
36
37 #define S_TYPEISSHM(st) (((st)->st_mode & ZVFS_MODE_IFMT) == ZVFS_MODE_IFSHM)
38
ZTEST(shm,test_shm_open)39 ZTEST(shm, test_shm_open)
40 {
41 int ret;
42 int fd[N];
43 struct stat st;
44
45 {
46 /* degenerate error cases */
47 zassert_not_ok(shm_open(NULL, INVALID_FLAGS, INVALID_MODE));
48 zassert_not_ok(shm_open(NULL, INVALID_FLAGS, VALID_MODE));
49 zassert_not_ok(shm_open(NULL, VALID_FLAGS, INVALID_MODE));
50 zassert_not_ok(shm_open(NULL, VALID_FLAGS, VALID_MODE));
51 zassert_not_ok(shm_open(INVALID_SHM_PATH, VALID_FLAGS, VALID_MODE));
52 zassert_not_ok(shm_open(EMPTY_SHM_PATH, VALID_FLAGS, VALID_MODE));
53 zassert_not_ok(shm_open(TOO_SHORT_SHM_PATH, VALID_FLAGS, VALID_MODE));
54 zassert_not_ok(shm_open(VALID_SHM_PATH, INVALID_FLAGS, INVALID_MODE));
55 zassert_not_ok(shm_open(VALID_SHM_PATH, INVALID_FLAGS, VALID_MODE));
56 zassert_not_ok(shm_open(VALID_SHM_PATH, VALID_FLAGS, INVALID_MODE));
57 }
58
59 /* open / close 1 file descriptor referring to VALID_SHM_PATH */
60 fd[0] = shm_open(VALID_SHM_PATH, VALID_FLAGS, VALID_MODE);
61 zassert_true(fd[0] >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH, VALID_FLAGS,
62 VALID_MODE, errno);
63
64 /* should have size 0 and be a shared memory object */
65 zassert_ok(fstat(fd[0], &st));
66 zassert_equal(st.st_size, 0);
67 zassert_true(S_TYPEISSHM(&st));
68
69 /* technically, the order of close / shm_unlink can be reversed too */
70 zassert_ok(close(fd[0]));
71 ret = shm_unlink(VALID_SHM_PATH);
72 zassert_true(ret == 0 || (ret == -1 && errno == ENOENT),
73 "unexpected return / errno from shm_unlink: %d / %d", ret, errno);
74
75 /* open / close N file descriptors referring to VALID_SHM_PATH */
76 for (size_t i = 0; i < N; ++i) {
77 fd[i] = shm_open(VALID_SHM_PATH, i == 0 ? CREATE_FLAGS : OPEN_FLAGS, VALID_MODE);
78 zassert_true(fd[i] >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH,
79 VALID_FLAGS, VALID_MODE, errno);
80 }
81 zassert_ok(shm_unlink(VALID_SHM_PATH));
82 for (size_t i = N; i > 0; --i) {
83 zassert_ok(close(fd[i - 1]));
84 }
85 }
86
ZTEST(shm,test_shm_unlink)87 ZTEST(shm, test_shm_unlink)
88 {
89 int fd;
90
91 {
92 /* degenerate error cases */
93 zassert_not_ok(shm_unlink(NULL));
94 zassert_not_ok(shm_unlink(INVALID_SHM_PATH));
95 zassert_not_ok(shm_unlink(EMPTY_SHM_PATH));
96 zassert_not_ok(shm_unlink(TOO_SHORT_SHM_PATH));
97 }
98
99 /* open / close 1 file descriptor referring to VALID_SHM_PATH */
100 fd = shm_open(VALID_SHM_PATH, VALID_FLAGS, VALID_MODE);
101 zassert_true(fd >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH, VALID_FLAGS,
102 VALID_MODE, errno);
103 /* technically, the order of close / shm_unlink can be reversed too */
104 zassert_ok(close(fd));
105 zassert_ok(shm_unlink(VALID_SHM_PATH));
106 /* should not be able to re-open the same path without O_CREAT */
107 zassert_not_ok(shm_open(VALID_SHM_PATH, OPEN_FLAGS, VALID_MODE));
108 }
109
ZTEST(shm,test_shm_read_write)110 ZTEST(shm, test_shm_read_write)
111 {
112 int fd[N];
113
114 for (size_t i = 0; i < N; ++i) {
115 char cbuf = 0xff;
116
117 fd[i] = shm_open(VALID_SHM_PATH, i == 0 ? CREATE_FLAGS : OPEN_FLAGS, VALID_MODE);
118 zassert_true(fd[i] >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH,
119 VALID_FLAGS, VALID_MODE, errno);
120 if (i == 0) {
121 /* size 0 on create / zero characters written */
122 zassert_equal(write(fd[0], "", 1), 0,
123 "write() should fail on newly create shm fd with size 0");
124 /* size 0 on create / zero characters read */
125 zassert_equal(read(fd[0], &cbuf, 1), 0,
126 "read() should fail on newly create shm fd with size 0");
127
128 BUILD_ASSERT(SHM_SIZE >= 1);
129 zassert_ok(ftruncate(fd[0], SHM_SIZE));
130
131 zassert_equal(write(fd[0], "\x42", 1), 1, "write() failed on fd %d: %d\n",
132 fd[0], errno);
133
134 continue;
135 }
136
137 zassert_equal(read(fd[i], &cbuf, 1), 1, "read() failed on fd %d: %d\n", fd[i],
138 errno);
139 zassert_equal(cbuf, 0x42,
140 "Failed to read byte over fd %d: expected: 0x%02x actual: 0x%02x",
141 fd[i], 0x42, cbuf);
142 }
143
144 for (size_t i = N; i > 0; --i) {
145 zassert_ok(close(fd[i - 1]));
146 }
147
148 zassert_ok(shm_unlink(VALID_SHM_PATH));
149 }
150
ZTEST(shm,test_shm_mmap)151 ZTEST(shm, test_shm_mmap)
152 {
153 int fd[N];
154 void *addr[N];
155
156 if (!IS_ENABLED(CONFIG_MMU)) {
157 ztest_test_skip();
158 }
159
160 for (size_t i = 0; i < N; ++i) {
161 fd[i] = shm_open(VALID_SHM_PATH, i == 0 ? CREATE_FLAGS : OPEN_FLAGS, VALID_MODE);
162 zassert_true(fd[i] >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH,
163 VALID_FLAGS, VALID_MODE, errno);
164
165 if (i == 0) {
166 /* cannot map shm of size zero */
167 zassert_not_ok(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
168 fd[0], 0));
169
170 zassert_ok(ftruncate(fd[0], PAGE_SIZE));
171 }
172
173 addr[i] = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0);
174 zassert_not_equal(MAP_FAILED, addr[i], "mmap() failed: %d", errno);
175
176 if ((i & 1) == 0) {
177 memset(addr[0], i & 0xff, PAGE_SIZE);
178 } else {
179 zassert_mem_equal(addr[i], addr[i - 1], PAGE_SIZE);
180 }
181 }
182
183 for (size_t i = N; i > 0; --i) {
184 zassert_ok(close(fd[i - 1]));
185 }
186
187 for (size_t i = N; i > 0; --i) {
188 zassert_ok(munmap(addr[i - 1], PAGE_SIZE));
189 /*
190 * Note: for some reason, in Zephyr, unmapping a physical page once, removes all
191 * virtual mappings. When that behaviour changes, remove the break below and adjust
192 * shm.c accordingly.
193 */
194 break;
195 }
196
197 zassert_ok(shm_unlink(VALID_SHM_PATH));
198 }
199
200 ZTEST_SUITE(shm, NULL, NULL, NULL, NULL, NULL);
201