diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 17:43:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 17:43:51 +0000 |
commit | be58c81aff4cd4c0ccf43dbd7998da4a6a08c03b (patch) | |
tree | 779c248fb61c83f65d1f0dc867f2053d76b4e03a /lib/debugfs/dev.c | |
parent | Initial commit. (diff) | |
download | arm-trusted-firmware-upstream.tar.xz arm-trusted-firmware-upstream.zip |
Adding upstream version 2.10.0+dfsg.upstream/2.10.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | lib/debugfs/dev.c | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/lib/debugfs/dev.c b/lib/debugfs/dev.c new file mode 100644 index 0000000..2fc1d40 --- /dev/null +++ b/lib/debugfs/dev.c @@ -0,0 +1,853 @@ +/* + * Copyright (c) 2019-2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <cdefs.h> +#include <common/debug.h> +#include <lib/debugfs.h> +#include <string.h> + +#include "dev.h" + +#define NR_MOUNT_POINTS 4 + +struct mount_point { + chan_t *new; + chan_t *old; +}; + +/* This array contains all the available channels of the filesystem. + * A file descriptor is the index of a specific channel in this array. + */ +static chan_t fdset[NR_CHANS]; + +/* This array contains all the available mount points of the filesystem. */ +static struct mount_point mount_points[NR_MOUNT_POINTS]; + +/* This variable stores the channel associated to the root directory. */ +static chan_t slash_channel; + +/* This function creates a channel from a device index and registers + * it to fdset. + */ +static chan_t *create_new_channel(unsigned char index) +{ + chan_t *channel = NULL; + int i; + + for (i = 0; i < NR_CHANS; i++) { + if (fdset[i].index == NODEV) { + channel = &fdset[i]; + channel->index = index; + break; + } + } + + return channel; +} + +/******************************************************************************* + * This function returns a pointer to an existing channel in fdset from a file + * descriptor. + ******************************************************************************/ +static chan_t *fd_to_channel(int fd) +{ + if ((fd < 0) || (fd >= NR_CHANS) || (fdset[fd].index == NODEV)) { + return NULL; + } + + return &fdset[fd]; +} + +/******************************************************************************* + * This function returns a file descriptor from a channel. + * The caller must be sure that the channel is registered in fdset. + ******************************************************************************/ +static int channel_to_fd(chan_t *channel) +{ + return (channel == NULL) ? -1 : (channel - fdset); +} + +/******************************************************************************* + * This function checks the validity of a mode. + ******************************************************************************/ +static bool is_valid_mode(int mode) +{ + if ((mode & O_READ) && (mode & (O_WRITE | O_RDWR))) { + return false; + } + if ((mode & O_WRITE) && (mode & (O_READ | O_RDWR))) { + return false; + } + if ((mode & O_RDWR) && (mode & (O_READ | O_WRITE))) { + return false; + } + + return true; +} + +/******************************************************************************* + * This function extracts the next part of the given path contained and puts it + * in token. It returns a pointer to the remainder of the path. + ******************************************************************************/ +static const char *next(const char *path, char *token) +{ + int index; + const char *cursor; + + while (*path == '/') { + ++path; + } + + index = 0; + cursor = path; + if (*path != '\0') { + while (*cursor != '/' && *cursor != '\0') { + if (index == NAMELEN) { + return NULL; + } + token[index++] = *cursor++; + } + } + token[index] = '\0'; + + return cursor; +} + +/******************************************************************************* + * This function returns the driver index in devtab of the driver + * identified by id. + ******************************************************************************/ +static int get_device_index(int id) +{ + int index; + dev_t * const *dp; + + for (index = 0, dp = devtab; *dp && (*dp)->id != id; ++dp) { + index++; + } + + if (*dp == NULL) { + return -1; + } + + return index; +} + +/******************************************************************************* + * This function clears a given channel fields + ******************************************************************************/ +static void channel_clear(chan_t *channel) +{ + channel->offset = 0; + channel->qid = 0; + channel->index = NODEV; + channel->dev = 0; + channel->mode = 0; +} + +/******************************************************************************* + * This function closes the channel pointed to by c. + ******************************************************************************/ +void channel_close(chan_t *channel) +{ + if (channel != NULL) { + channel_clear(channel); + } +} + +/******************************************************************************* + * This function copies data from src to dst after applying the offset of the + * channel c. nbytes bytes are expected to be copied unless the data goes over + * dst + len. + * It returns the actual number of bytes that were copied. + ******************************************************************************/ +int buf_to_channel(chan_t *channel, void *dst, void *src, int nbytes, long len) +{ + const char *addr = src; + + if ((channel == NULL) || (dst == NULL) || (src == NULL)) { + return 0; + } + + if (channel->offset >= len) { + return 0; + } + + if ((channel->offset + nbytes) > len) { + nbytes = len - channel->offset; + } + + memcpy(dst, addr + channel->offset, nbytes); + + channel->offset += nbytes; + + return nbytes; +} + +/******************************************************************************* + * This function checks whether a channel (identified by its device index and + * qid) is registered as a mount point. + * Returns a pointer to the channel it is mounted to when found, NULL otherwise. + ******************************************************************************/ +static chan_t *mount_point_to_channel(int index, qid_t qid) +{ + chan_t *channel; + struct mount_point *mp; + + for (mp = mount_points; mp < &mount_points[NR_MOUNT_POINTS]; mp++) { + channel = mp->new; + if (channel == NULL) { + continue; + } + + if ((channel->index == index) && (channel->qid == qid)) { + return mp->old; + } + } + + return NULL; +} + +/******************************************************************************* + * This function calls the attach function of the driver identified by id. + ******************************************************************************/ +chan_t *attach(int id, int dev) +{ + /* Get the devtab index for the driver identified by id */ + int index = get_device_index(id); + + if (index < 0) { + return NULL; + } + + return devtab[index]->attach(id, dev); +} + +/******************************************************************************* + * This function is the default implementation of the driver attach function. + * It creates a new channel and returns a pointer to it. + ******************************************************************************/ +chan_t *devattach(int id, int dev) +{ + chan_t *channel; + int index; + + index = get_device_index(id); + if (index < 0) { + return NULL; + } + + channel = create_new_channel(index); + if (channel == NULL) { + return NULL; + } + + channel->dev = dev; + channel->qid = CHDIR; + + return channel; +} + +/******************************************************************************* + * This function returns a channel given a path. + * It goes through the filesystem, from the root namespace ('/') or from a + * device namespace ('#'), switching channel on mount points. + ******************************************************************************/ +chan_t *path_to_channel(const char *path, int mode) +{ + int i, n; + const char *path_next; + chan_t *mnt, *channel; + char elem[NAMELEN]; + + if (path == NULL) { + return NULL; + } + + switch (path[0]) { + case '/': + channel = clone(&slash_channel, NULL); + path_next = path; + break; + case '#': + path_next = next(path + 1, elem); + if (path_next == NULL) { + goto noent; + } + + n = 0; + for (i = 1; (elem[i] >= '0') && (elem[i] <= '9'); i++) { + n += elem[i] - '0'; + } + + if (elem[i] != '\0') { + goto noent; + } + + channel = attach(elem[0], n); + break; + default: + return NULL; + } + + if (channel == NULL) { + return NULL; + } + + for (path_next = next(path_next, elem); *elem; + path_next = next(path_next, elem)) { + if ((channel->qid & CHDIR) == 0) { + goto notfound; + } + + if (devtab[channel->index]->walk(channel, elem) < 0) { + channel_close(channel); + goto notfound; + } + + mnt = mount_point_to_channel(channel->index, channel->qid); + if (mnt != NULL) { + clone(mnt, channel); + } + } + + if (path_next == NULL) { + goto notfound; + } + + /* TODO: check mode */ + return channel; + +notfound: + channel_close(channel); +noent: + return NULL; +} + +/******************************************************************************* + * This function calls the clone function of the driver associated to the + * channel c. + ******************************************************************************/ +chan_t *clone(chan_t *c, chan_t *nc) +{ + if (c->index == NODEV) { + return NULL; + } + + return devtab[c->index]->clone(c, nc); +} + +/******************************************************************************* + * This function is the default implementation of the driver clone function. + * It creates a new channel and returns a pointer to it. + * It clones channel into new_channel. + ******************************************************************************/ +chan_t *devclone(chan_t *channel, chan_t *new_channel) +{ + if (channel == NULL) { + return NULL; + } + + if (new_channel == NULL) { + new_channel = create_new_channel(channel->index); + if (new_channel == NULL) { + return NULL; + } + } + + new_channel->qid = channel->qid; + new_channel->dev = channel->dev; + new_channel->mode = channel->mode; + new_channel->offset = channel->offset; + new_channel->index = channel->index; + + return new_channel; +} + +/******************************************************************************* + * This function is the default implementation of the driver walk function. + * It goes through all the elements of tab using the gen function until a match + * is found with name. + * If a match is found, it copies the qid of the new directory. + ******************************************************************************/ +int devwalk(chan_t *channel, const char *name, const dirtab_t *tab, + int ntab, devgen_t *gen) +{ + int i; + dir_t dir; + + if ((channel == NULL) || (name == NULL) || (gen == NULL)) { + return -1; + } + + if ((name[0] == '.') && (name[1] == '\0')) { + return 1; + } + + for (i = 0; ; i++) { + switch ((*gen)(channel, tab, ntab, i, &dir)) { + case 0: + /* Intentional fall-through */ + case -1: + return -1; + case 1: + if (strncmp(name, dir.name, NAMELEN) != 0) { + continue; + } + channel->qid = dir.qid; + return 1; + } + } +} + +/******************************************************************************* + * This is a helper function which exposes the content of a directory, element + * by element. It is meant to be called until the end of the directory is + * reached or an error occurs. + * It returns -1 on error, 0 on end of directory and 1 when a new file is found. + ******************************************************************************/ +int dirread(chan_t *channel, dir_t *dir, const dirtab_t *tab, + int ntab, devgen_t *gen) +{ + int i, ret; + + if ((channel == NULL) || (dir == NULL) || (gen == NULL)) { + return -1; + } + + i = channel->offset/sizeof(dir_t); + ret = (*gen)(channel, tab, ntab, i, dir); + if (ret == 1) { + channel->offset += sizeof(dir_t); + } + + return ret; +} + +/******************************************************************************* + * This function sets the elements of dir. + ******************************************************************************/ +void make_dir_entry(chan_t *channel, dir_t *dir, + const char *name, long length, qid_t qid, unsigned int mode) +{ + if ((channel == NULL) || (dir == NULL) || (name == NULL)) { + return; + } + + strlcpy(dir->name, name, sizeof(dir->name)); + dir->length = length; + dir->qid = qid; + dir->mode = mode; + + if ((qid & CHDIR) != 0) { + dir->mode |= O_DIR; + } + + dir->index = channel->index; + dir->dev = channel->dev; +} + +/******************************************************************************* + * This function is the default implementation of the internal driver gen + * function. + * It copies and formats the information of the nth element of tab into dir. + ******************************************************************************/ +int devgen(chan_t *channel, const dirtab_t *tab, int ntab, int n, dir_t *dir) +{ + const dirtab_t *dp; + + if ((channel == NULL) || (dir == NULL) || (tab == NULL) || + (n >= ntab)) { + return 0; + } + + dp = &tab[n]; + make_dir_entry(channel, dir, dp->name, dp->length, dp->qid, dp->perm); + return 1; +} + +/******************************************************************************* + * This function returns a file descriptor identifying the channel associated to + * the given path. + ******************************************************************************/ +int open(const char *path, int mode) +{ + chan_t *channel; + + if (path == NULL) { + return -1; + } + + if (is_valid_mode(mode) == false) { + return -1; + } + + channel = path_to_channel(path, mode); + + return channel_to_fd(channel); +} + +/******************************************************************************* + * This function closes the channel identified by the file descriptor fd. + ******************************************************************************/ +int close(int fd) +{ + chan_t *channel; + + channel = fd_to_channel(fd); + if (channel == NULL) { + return -1; + } + + channel_close(channel); + return 0; +} + +/******************************************************************************* + * This function is the default implementation of the driver stat function. + * It goes through all the elements of tab using the gen function until a match + * is found with file. + * If a match is found, dir contains the information file. + ******************************************************************************/ +int devstat(chan_t *dirc, const char *file, dir_t *dir, + const dirtab_t *tab, int ntab, devgen_t *gen) +{ + int i, r = 0; + chan_t *c, *mnt; + + if ((dirc == NULL) || (dir == NULL) || (gen == NULL)) { + return -1; + } + + c = path_to_channel(file, O_STAT); + if (c == NULL) { + return -1; + } + + for (i = 0; ; i++) { + switch ((*gen)(dirc, tab, ntab, i, dir)) { + case 0: + /* Intentional fall-through */ + case -1: + r = -1; + goto leave; + case 1: + mnt = mount_point_to_channel(dir->index, dir->qid); + if (mnt != NULL) { + dir->qid = mnt->qid; + dir->index = mnt->index; + } + + if ((dir->qid != c->qid) || (dir->index != c->index)) { + continue; + } + + goto leave; + } + } + +leave: + channel_close(c); + return r; +} + +/******************************************************************************* + * This function calls the stat function of the driver associated to the parent + * directory of the file in path. + * The result is stored in dir. + ******************************************************************************/ +int stat(const char *path, dir_t *dir) +{ + int r; + size_t len; + chan_t *channel; + char *p, dirname[PATHLEN]; + + if ((path == NULL) || (dir == NULL)) { + return -1; + } + + len = strlen(path); + if ((len + 1) > sizeof(dirname)) { + return -1; + } + + memcpy(dirname, path, len); + for (p = dirname + len; p > dirname; --p) { + if (*p != '/') { + break; + } + } + + p = memrchr(dirname, '/', p - dirname); + if (p == NULL) { + return -1; + } + + dirname[p - dirname + 1] = '\0'; + + channel = path_to_channel(dirname, O_STAT); + if (channel == NULL) { + return -1; + } + + r = devtab[channel->index]->stat(channel, path, dir); + channel_close(channel); + + return r; +} + +/******************************************************************************* + * This function calls the read function of the driver associated to fd. + * It fills buf with at most n bytes. + * It returns the number of bytes that were actually read. + ******************************************************************************/ +int read(int fd, void *buf, int n) +{ + chan_t *channel; + + if (buf == NULL) { + return -1; + } + + channel = fd_to_channel(fd); + if (channel == NULL) { + return -1; + } + + if (((channel->qid & CHDIR) != 0) && (n < sizeof(dir_t))) { + return -1; + } + + return devtab[channel->index]->read(channel, buf, n); +} + +/******************************************************************************* + * This function calls the write function of the driver associated to fd. + * It writes at most n bytes of buf. + * It returns the number of bytes that were actually written. + ******************************************************************************/ +int write(int fd, void *buf, int n) +{ + chan_t *channel; + + if (buf == NULL) { + return -1; + } + + channel = fd_to_channel(fd); + if (channel == NULL) { + return -1; + } + + if ((channel->qid & CHDIR) != 0) { + return -1; + } + + return devtab[channel->index]->write(channel, buf, n); +} + +/******************************************************************************* + * This function calls the seek function of the driver associated to fd. + * It applies the offset off according to the strategy whence. + ******************************************************************************/ +int seek(int fd, long off, int whence) +{ + chan_t *channel; + + channel = fd_to_channel(fd); + if (channel == NULL) { + return -1; + } + + if ((channel->qid & CHDIR) != 0) { + return -1; + } + + return devtab[channel->index]->seek(channel, off, whence); +} + +/******************************************************************************* + * This function is the default error implementation of the driver mount + * function. + ******************************************************************************/ +chan_t *deverrmount(chan_t *channel, const char *spec) +{ + return NULL; +} + +/******************************************************************************* + * This function is the default error implementation of the driver write + * function. + ******************************************************************************/ +int deverrwrite(chan_t *channel, void *buf, int n) +{ + return -1; +} + +/******************************************************************************* + * This function is the default error implementation of the driver seek + * function. + ******************************************************************************/ +int deverrseek(chan_t *channel, long off, int whence) +{ + return -1; +} + +/******************************************************************************* + * This function is the default implementation of the driver seek function. + * It applies the offset off according to the strategy whence to the channel c. + ******************************************************************************/ +int devseek(chan_t *channel, long off, int whence) +{ + switch (whence) { + case KSEEK_SET: + channel->offset = off; + break; + case KSEEK_CUR: + channel->offset += off; + break; + case KSEEK_END: + /* Not implemented */ + return -1; + } + + return 0; +} + +/******************************************************************************* + * This function registers the channel associated to the path new as a mount + * point for the channel c. + ******************************************************************************/ +static int add_mount_point(chan_t *channel, const char *new) +{ + int i; + chan_t *cn; + struct mount_point *mp; + + if (new == NULL) { + goto err0; + } + + cn = path_to_channel(new, O_READ); + if (cn == NULL) { + goto err0; + } + + if ((cn->qid & CHDIR) == 0) { + goto err1; + } + + for (i = NR_MOUNT_POINTS - 1; i >= 0; i--) { + mp = &mount_points[i]; + if (mp->new == NULL) { + break; + } + } + + if (i < 0) { + goto err1; + } + + mp->new = cn; + mp->old = channel; + + return 0; + +err1: + channel_close(cn); +err0: + return -1; +} + +/******************************************************************************* + * This function registers the path new as a mount point for the path old. + ******************************************************************************/ +int bind(const char *old, const char *new) +{ + chan_t *channel; + + channel = path_to_channel(old, O_BIND); + if (channel == NULL) { + return -1; + } + + if (add_mount_point(channel, new) < 0) { + channel_close(channel); + return -1; + } + + return 0; +} + +/******************************************************************************* + * This function calls the mount function of the driver associated to the path + * srv. + * It mounts the path srv on the path where. + ******************************************************************************/ +int mount(const char *srv, const char *where, const char *spec) +{ + chan_t *channel, *mount_point_chan; + int ret; + + channel = path_to_channel(srv, O_RDWR); + if (channel == NULL) { + goto err0; + } + + mount_point_chan = devtab[channel->index]->mount(channel, spec); + if (mount_point_chan == NULL) { + goto err1; + } + + ret = add_mount_point(mount_point_chan, where); + if (ret < 0) { + goto err2; + } + + channel_close(channel); + + return 0; + +err2: + channel_close(mount_point_chan); +err1: + channel_close(channel); +err0: + return -1; +} + +/******************************************************************************* + * This function initializes the device environment. + * It creates the '/' channel. + * It links the device drivers to the physical drivers. + ******************************************************************************/ +void debugfs_init(void) +{ + chan_t *channel, *cloned_channel; + + for (channel = fdset; channel < &fdset[NR_CHANS]; channel++) { + channel_clear(channel); + } + + channel = devattach('/', 0); + if (channel == NULL) { + panic(); + } + + cloned_channel = clone(channel, &slash_channel); + if (cloned_channel == NULL) { + panic(); + } + + channel_close(channel); + devlink(); +} + +__dead2 void devpanic(const char *cause) +{ + panic(); +} |