289 lines
7.1 KiB
C
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;
|
|
}
|