| /* |
| * Copyright (c) 2024, Tenstorrent AI ULC |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <sys/mman.h> |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/net/socket.h> |
| #include <zephyr/posix/fcntl.h> |
| #include <zephyr/sys/fdtable.h> |
| |
| #include <zephyr/ztest.h> |
| |
| #define SHM_SIZE 8 |
| |
| #define VALID_SHM_PATH "/foo" |
| #define INVALID_SHM_PATH "foo" |
| #define EMPTY_SHM_PATH "" |
| #define TOO_SHORT_SHM_PATH "/" |
| |
| #define INVALID_MODE 0 |
| #define VALID_MODE 0666 |
| |
| #define INVALID_FLAGS 0 |
| #define VALID_FLAGS (O_RDWR | O_CREAT) |
| #define CREATE_FLAGS VALID_FLAGS |
| #define OPEN_FLAGS (VALID_FLAGS & ~O_CREAT) |
| |
| /* account for stdin, stdout, stderr */ |
| #define N (CONFIG_ZVFS_OPEN_MAX - 3) |
| |
| /* we need to have at least 2 shared memory objects */ |
| BUILD_ASSERT(N >= 2, "CONFIG_ZVFS_OPEN_MAX must be > 4"); |
| |
| #define S_TYPEISSHM(st) (((st)->st_mode & ZVFS_MODE_IFMT) == ZVFS_MODE_IFSHM) |
| |
| ZTEST(xsi_realtime, test_shm_open) |
| { |
| int ret; |
| int fd[N]; |
| struct stat st; |
| |
| { |
| /* degenerate error cases */ |
| zassert_not_ok(shm_open(NULL, INVALID_FLAGS, INVALID_MODE)); |
| zassert_not_ok(shm_open(NULL, INVALID_FLAGS, VALID_MODE)); |
| zassert_not_ok(shm_open(NULL, VALID_FLAGS, INVALID_MODE)); |
| zassert_not_ok(shm_open(NULL, VALID_FLAGS, VALID_MODE)); |
| zassert_not_ok(shm_open(INVALID_SHM_PATH, VALID_FLAGS, VALID_MODE)); |
| zassert_not_ok(shm_open(EMPTY_SHM_PATH, VALID_FLAGS, VALID_MODE)); |
| zassert_not_ok(shm_open(TOO_SHORT_SHM_PATH, VALID_FLAGS, VALID_MODE)); |
| zassert_not_ok(shm_open(VALID_SHM_PATH, INVALID_FLAGS, INVALID_MODE)); |
| zassert_not_ok(shm_open(VALID_SHM_PATH, INVALID_FLAGS, VALID_MODE)); |
| zassert_not_ok(shm_open(VALID_SHM_PATH, VALID_FLAGS, INVALID_MODE)); |
| } |
| |
| /* open / close 1 file descriptor referring to VALID_SHM_PATH */ |
| fd[0] = shm_open(VALID_SHM_PATH, VALID_FLAGS, VALID_MODE); |
| zassert_true(fd[0] >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH, VALID_FLAGS, |
| VALID_MODE, errno); |
| |
| /* should have size 0 and be a shared memory object */ |
| zassert_ok(fstat(fd[0], &st)); |
| zassert_equal(st.st_size, 0); |
| zassert_true(S_TYPEISSHM(&st)); |
| |
| /* technically, the order of close / shm_unlink can be reversed too */ |
| zassert_ok(close(fd[0])); |
| ret = shm_unlink(VALID_SHM_PATH); |
| zassert_true(ret == 0 || (ret == -1 && errno == ENOENT), |
| "unexpected return / errno from shm_unlink: %d / %d", ret, errno); |
| |
| /* open / close N file descriptors referring to VALID_SHM_PATH */ |
| for (size_t i = 0; i < N; ++i) { |
| fd[i] = shm_open(VALID_SHM_PATH, i == 0 ? CREATE_FLAGS : OPEN_FLAGS, VALID_MODE); |
| zassert_true(fd[i] >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH, |
| VALID_FLAGS, VALID_MODE, errno); |
| } |
| zassert_ok(shm_unlink(VALID_SHM_PATH)); |
| for (size_t i = N; i > 0; --i) { |
| zassert_ok(close(fd[i - 1])); |
| } |
| } |
| |
| ZTEST(xsi_realtime, test_shm_unlink) |
| { |
| int fd; |
| |
| { |
| /* degenerate error cases */ |
| zassert_not_ok(shm_unlink(NULL)); |
| zassert_not_ok(shm_unlink(INVALID_SHM_PATH)); |
| zassert_not_ok(shm_unlink(EMPTY_SHM_PATH)); |
| zassert_not_ok(shm_unlink(TOO_SHORT_SHM_PATH)); |
| } |
| |
| /* open / close 1 file descriptor referring to VALID_SHM_PATH */ |
| fd = shm_open(VALID_SHM_PATH, VALID_FLAGS, VALID_MODE); |
| zassert_true(fd >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH, VALID_FLAGS, |
| VALID_MODE, errno); |
| /* technically, the order of close / shm_unlink can be reversed too */ |
| zassert_ok(close(fd)); |
| zassert_ok(shm_unlink(VALID_SHM_PATH)); |
| /* should not be able to re-open the same path without O_CREAT */ |
| zassert_not_ok(shm_open(VALID_SHM_PATH, OPEN_FLAGS, VALID_MODE)); |
| } |
| |
| ZTEST(xsi_realtime, test_shm_read_write) |
| { |
| int fd[N]; |
| |
| for (size_t i = 0; i < N; ++i) { |
| char cbuf = 0xff; |
| |
| fd[i] = shm_open(VALID_SHM_PATH, i == 0 ? CREATE_FLAGS : OPEN_FLAGS, VALID_MODE); |
| zassert_true(fd[i] >= 0, "shm_open(%s, %x, %04o) failed: %d", VALID_SHM_PATH, |
| VALID_FLAGS, VALID_MODE, errno); |
| if (i == 0) { |
| /* size 0 on create / zero characters written */ |
| zassert_equal(write(fd[0], "", 1), 0, |
| "write() should fail on newly create shm fd with size 0"); |
| /* size 0 on create / zero characters read */ |
| zassert_equal(read(fd[0], &cbuf, 1), 0, |
| "read() should fail on newly create shm fd with size 0"); |
| |
| BUILD_ASSERT(SHM_SIZE >= 1); |
| zassert_ok(ftruncate(fd[0], SHM_SIZE)); |
| |
| zassert_equal(write(fd[0], "\x42", 1), 1, "write() failed on fd %d: %d\n", |
| fd[0], errno); |
| |
| continue; |
| } |
| |
| zassert_equal(read(fd[i], &cbuf, 1), 1, "read() failed on fd %d: %d\n", fd[i], |
| errno); |
| zassert_equal(cbuf, 0x42, |
| "Failed to read byte over fd %d: expected: 0x%02x actual: 0x%02x", |
| fd[i], 0x42, cbuf); |
| } |
| |
| for (size_t i = N; i > 0; --i) { |
| zassert_ok(close(fd[i - 1])); |
| } |
| |
| zassert_ok(shm_unlink(VALID_SHM_PATH)); |
| } |
| |
| ZTEST(xsi_realtime, test_shm_mmap) |
| { |
| int fd[N]; |
| void *addr[N]; |
| |
| if (!IS_ENABLED(CONFIG_MMU)) { |
| ztest_test_skip(); |
| } |
| |
| for (size_t i = 0; i < N; ++i) { |
| fd[i] = shm_open(VALID_SHM_PATH, i == 0 ? CREATE_FLAGS : OPEN_FLAGS, VALID_MODE); |
| zassert_true(fd[i] >= 0, "shm_open(%s, %x, %04o) failed : %d", VALID_SHM_PATH, |
| VALID_FLAGS, VALID_MODE, errno); |
| |
| if (i == 0) { |
| /* cannot map shm of size zero */ |
| zassert_not_ok(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, |
| fd[0], 0)); |
| |
| zassert_ok(ftruncate(fd[0], PAGE_SIZE)); |
| } |
| |
| addr[i] = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0); |
| zassert_not_equal(MAP_FAILED, addr[i], "mmap() failed: %d", errno); |
| |
| if ((i & 1) == 0) { |
| memset(addr[0], i & 0xff, PAGE_SIZE); |
| } else { |
| zassert_mem_equal(addr[i], addr[i - 1], PAGE_SIZE); |
| } |
| } |
| |
| for (size_t i = N; i > 0; --i) { |
| zassert_ok(close(fd[i - 1])); |
| } |
| |
| for (size_t i = N; i > 0; --i) { |
| zassert_ok(munmap(addr[i - 1], PAGE_SIZE)); |
| /* |
| * Note: for some reason, in Zephyr, unmapping a physical page once, removes all |
| * virtual mappings. When that behaviour changes, remove the break below and adjust |
| * shm.c accordingly. |
| */ |
| break; |
| } |
| |
| zassert_ok(shm_unlink(VALID_SHM_PATH)); |
| } |