Files
checker_m4/source/OpenAMP/libmetal/lib/system/linux/utilities.c
2023-06-21 18:00:56 +08:00

289 lines
7.1 KiB
C

/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file utils.c
* @brief Linux libmetal utility functions.
*/
#include <metal/utilities.h>
#include <metal/sys.h>
/**
* @brief Open (or create) a file.
*
* This function opens or creates a file with read/write permissions and the
* O_CLOEXEC flag set.
*
* @param[in] path File path to open.
* @param[in] shm Open shared memory (via shm_open) if non-zero.
* @return File descriptor.
*/
int metal_open(const char *path, int shm)
{
const int flags = O_RDWR | O_CREAT | O_CLOEXEC;
const int mode = S_IRUSR | S_IWUSR;
int fd;
if (!path || !strlen(path))
return -EINVAL;
fd = shm ? shm_open(path, flags, mode) : open(path, flags, mode);
return fd < 0 ? -errno : fd;
}
/**
* @brief Open (or create) and unlink a file.
*
* This function opens or creates a file with read/write permissions and the
* O_CLOEXEC flag set. This file is then unlinked to ensure that it is removed
* when the last reference to the file is dropped. This ensures that libmetal
* shared maps are released appropriately once all users of the shared data
* exit.
*
* @param[in] path File path to open.
* @param[in] shm Open shared memory (via shm_open) if non-zero.
* @return File descriptor.
*/
int metal_open_unlinked(const char *path, int shm)
{
int fd, error;
fd = metal_open(path, shm);
if (fd < 0)
return fd;
error = shm ? shm_unlink(path) : unlink(path);
error = error < 0 ? -errno : 0;
if (error)
close(fd);
return error ? error : fd;
}
/**
* @brief Randomize a string.
*
* This function randomizes the contents of a string.
*
* @param[in] template String to be randomized.
*/
void metal_randomize_string(char *template)
{
const char *chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-=";
while (*template) {
*template++ = chars[rand() % 64];
}
}
/**
* @brief Create a file name template suitable for use with metal_mktemp.
*
* @param[out] template Template string to be filled in.
* @param[in] name Module/user identifier used as part of the
* generated template.
*/
void metal_mktemp_template(char template[PATH_MAX], const char *name)
{
snprintf(template, PATH_MAX, "%s/metal-%s-XXXXXX",
_metal.tmp_path, name);
}
/**
* @brief Create a temporary file or fifo.
*
* This function creates a temporary file or fifo, and sets the O_CLOEXEC flag
* on it.
*
* @param[in] template File name template (the last 6 characters must
* be XXXXXX per mkstemp requirements).
* @param[in] fifo Non-zero to create a FIFO instead of a regular
* file.
* @return File descriptor.
*/
int metal_mktemp(char *template, int fifo)
{
const int mode = S_IRUSR | S_IWUSR;
int result, len, flags;
char *suffix;
if (!template)
return -EINVAL;
len = strlen(template);
suffix = &template[len - 6];
if (len < 6 || strcmp(suffix, "XXXXXX")) {
metal_log(METAL_LOG_ERROR, "template %s has no trailing pattern\n",
template);
return -EINVAL;
}
flags = (fifo
? O_RDONLY | O_NONBLOCK | O_CLOEXEC
: O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC);
for (;;) {
metal_randomize_string(suffix);
if (fifo) {
result = mkfifo(template, mode);
if (result < 0) {
if (errno == EEXIST)
continue;
metal_log(METAL_LOG_ERROR, "mkfifo(%s) failed (%s)\n",
template, strerror(errno));
return -errno;
}
}
result = open(template, flags, mode);
if (result < 0) {
if (fifo)
unlink(template);
if (errno == EEXIST)
continue;
metal_log(METAL_LOG_ERROR, "open() failed (%s)\n",
strerror(errno));
return -errno;
}
return result;
}
}
/**
* @brief Open (or create) and unlink a temporary file.
*
* This function creates a temporary file, and sets the O_CLOEXEC flag on it.
* O_CLOEXEC flag set. This file is then unlinked to ensure that it is removed
* when the last reference to the file is dropped. This ensures that libmetal
* shared maps are released appropriately once all users of the shared data
* exit.
*
* @param[in] template File name template (the last 6 characters must
* be XXXXXX per mkstemp requirements).
* @return File descriptor.
*/
int metal_mktemp_unlinked(char *template)
{
int fd, error;
fd = metal_mktemp(template, 0);
if (fd < 0)
return fd;
error = unlink(template) < 0 ? -errno : 0;
if (error)
close(fd);
return error ? error : fd;
}
/**
* @brief Map a segment of a file/device.
*
* This function maps a segment of a file or device into the process address
* space, after optionally expanding the file if necessary. If required, the
* file is expanded to hold the requested map area. This is done under and
* advisory lock, and therefore the called must not have an advisory lock on
* the file being mmapped.
*
* @param[in] fd File descriptor to map.
* @param[in] offset Offset in file to map.
* @param[in] size Size of region to map.
* @param[in] expand Allow file expansion via ftruncate if non-zero.
* @param[in] flags Flags for mmap(), MAP_SHARED included implicitly.
* @param[out] result Returned pointer to new memory map.
* @return 0 on success, or -errno on error.
*/
int metal_map(int fd, off_t offset, size_t size, int expand, int flags,
void **result)
{
int prot = PROT_READ | PROT_WRITE, error;
void *mem;
flags |= MAP_SHARED;
if (fd < 0) {
fd = -1;
flags = MAP_PRIVATE | MAP_ANONYMOUS;
} else if (expand) {
off_t reqsize = offset + size;
struct stat stat;
error = flock(fd, LOCK_EX) < 0 ? -errno : 0;
if (!error)
error = fstat(fd, &stat);
if (!error && stat.st_size < reqsize)
error = ftruncate(fd, reqsize);
if (!error)
flock(fd, LOCK_UN);
if (error)
return -errno;
}
mem = mmap(NULL, size, prot, flags, fd, offset);
if (mem == MAP_FAILED)
return -errno;
*result = mem;
return 0;
}
/**
* @brief Unmap a segment of the process address space.
*
* This function unmaps a segment of the process address space.
*
* @param[in] mem Segment to unmap.
* @param[in] size Size of region to unmap.
* @return 0 on success, or -errno on error.
*/
int metal_unmap(void *mem, size_t size)
{
return munmap(mem, size) < 0 ? -errno : 0;
}
/**
* @brief Lock in a region of the process address space.
*
* @param[in] mem Pointer to start of region.
* @param[in] size Size of region.
* @return 0 on success, or -errno on error.
*/
int metal_mlock(void *mem, size_t size)
{
return mlock(mem, size) ? -errno : 0;
}
int metal_virt2phys(void *addr, unsigned long *phys)
{
off_t offset;
uint64_t entry;
int error;
if (_metal.pagemap_fd < 0)
return -ENOSYS;
offset = ((uintptr_t)addr >> _metal.page_shift) * sizeof(entry);
error = pread(_metal.pagemap_fd, &entry, sizeof(entry), offset);
if (error < 0) {
metal_log(METAL_LOG_ERROR, "failed pagemap pread (offset %llx) - %s\n",
(unsigned long long)offset, strerror(errno));
return -errno;
}
/* Check page present and not swapped. */
if ((entry >> 62) != 2) {
metal_log(METAL_LOG_ERROR, "pagemap page not present, %llx -> %llx\n",
(unsigned long long)offset, (unsigned long long)entry);
return -ENOENT;
}
*phys = (entry & ((1ULL << 54) - 1)) << _metal.page_shift;
return 0;
}