diff options
Diffstat (limited to 'lib/debugfs')
-rw-r--r-- | lib/debugfs/blobs.h | 12 | ||||
-rw-r--r-- | lib/debugfs/debugfs.mk | 13 | ||||
-rw-r--r-- | lib/debugfs/debugfs_smc.c | 209 | ||||
-rw-r--r-- | lib/debugfs/dev.c | 853 | ||||
-rw-r--r-- | lib/debugfs/dev.h | 120 | ||||
-rw-r--r-- | lib/debugfs/devc.c | 20 | ||||
-rw-r--r-- | lib/debugfs/devfip.c | 321 | ||||
-rw-r--r-- | lib/debugfs/devroot.c | 97 |
8 files changed, 1645 insertions, 0 deletions
diff --git a/lib/debugfs/blobs.h b/lib/debugfs/blobs.h new file mode 100644 index 0000000..54ca9f7 --- /dev/null +++ b/lib/debugfs/blobs.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "dev.h" + +static const dirtab_t blobtab[] = { + {"ctl", DEV_ROOT_QBLOBCTL, 0, O_READ}, + {"fip.bin", DEV_ROOT_QBLOBCTL + 1, 0x100000, O_READ, (void *)0x8000000} +}; diff --git a/lib/debugfs/debugfs.mk b/lib/debugfs/debugfs.mk new file mode 100644 index 0000000..caa9fcf --- /dev/null +++ b/lib/debugfs/debugfs.mk @@ -0,0 +1,13 @@ +# +# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +DEBUGFS_SRCS := $(addprefix lib/debugfs/, \ + dev.c \ + devc.c \ + devroot.c \ + devfip.c) + +DEBUGFS_SRCS += lib/debugfs/debugfs_smc.c diff --git a/lib/debugfs/debugfs_smc.c b/lib/debugfs/debugfs_smc.c new file mode 100644 index 0000000..64c64f8 --- /dev/null +++ b/lib/debugfs/debugfs_smc.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <lib/debugfs.h> +#include <lib/smccc.h> +#include <lib/spinlock.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <smccc_helpers.h> + +#define MAX_PATH_LEN 256 + +#define MOUNT 0 +#define CREATE 1 +#define OPEN 2 +#define CLOSE 3 +#define READ 4 +#define WRITE 5 +#define SEEK 6 +#define BIND 7 +#define STAT 8 +#define INIT 10 +#define VERSION 11 + +/* This is the virtual address to which we map the NS shared buffer */ +#define DEBUGFS_SHARED_BUF_VIRT ((void *)0x81000000U) + +static union debugfs_parms { + struct { + char fname[MAX_PATH_LEN]; + } open; + + struct { + char srv[MAX_PATH_LEN]; + char where[MAX_PATH_LEN]; + char spec[MAX_PATH_LEN]; + } mount; + + struct { + char path[MAX_PATH_LEN]; + dir_t dir; + } stat; + + struct { + char oldpath[MAX_PATH_LEN]; + char newpath[MAX_PATH_LEN]; + } bind; +} parms; + +/* debugfs_access_lock protects shared buffer and internal */ +/* FS functions from concurrent accesses. */ +static spinlock_t debugfs_access_lock; + +static bool debugfs_initialized; + +uintptr_t debugfs_smc_handler(unsigned int smc_fid, + u_register_t cmd, + u_register_t arg2, + u_register_t arg3, + u_register_t arg4, + void *cookie, + void *handle, + u_register_t flags) +{ + int64_t smc_ret = DEBUGFS_E_INVALID_PARAMS, smc_resp = 0; + int ret; + + /* Allow calls from non-secure only */ + if (is_caller_secure(flags)) { + SMC_RET1(handle, DEBUGFS_E_DENIED); + } + + /* Expect a SiP service fast call */ + if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) || + (GET_SMC_OEN(smc_fid) != OEN_SIP_START)) { + SMC_RET1(handle, SMC_UNK); + } + + /* Truncate parameters if 32b SMC convention call */ + if (GET_SMC_CC(smc_fid) == SMC_32) { + arg2 &= 0xffffffff; + arg3 &= 0xffffffff; + arg4 &= 0xffffffff; + } + + spin_lock(&debugfs_access_lock); + + if (debugfs_initialized == true) { + /* Copy NS shared buffer to internal secure location */ + memcpy(&parms, (void *)DEBUGFS_SHARED_BUF_VIRT, + sizeof(union debugfs_parms)); + } + + switch (cmd) { + case INIT: + if (debugfs_initialized == false) { + /* TODO: check PA validity e.g. whether */ + /* it is an NS region. */ + ret = mmap_add_dynamic_region(arg2, + (uintptr_t)DEBUGFS_SHARED_BUF_VIRT, + PAGE_SIZE_4KB, + MT_MEMORY | MT_RW | MT_NS); + if (ret == 0) { + debugfs_initialized = true; + smc_ret = SMC_OK; + smc_resp = 0; + } + } + break; + + case VERSION: + smc_ret = SMC_OK; + smc_resp = DEBUGFS_VERSION; + break; + + case MOUNT: + ret = mount(parms.mount.srv, + parms.mount.where, + parms.mount.spec); + if (ret == 0) { + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + case OPEN: + ret = open(parms.open.fname, arg2); + if (ret >= 0) { + smc_ret = SMC_OK; + smc_resp = ret; + } + break; + + case CLOSE: + ret = close(arg2); + if (ret == 0) { + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + case READ: + ret = read(arg2, DEBUGFS_SHARED_BUF_VIRT, arg3); + if (ret >= 0) { + smc_ret = SMC_OK; + smc_resp = ret; + } + break; + + case SEEK: + ret = seek(arg2, arg3, arg4); + if (ret == 0) { + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + case BIND: + ret = bind(parms.bind.oldpath, parms.bind.newpath); + if (ret == 0) { + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + case STAT: + ret = stat(parms.stat.path, &parms.stat.dir); + if (ret == 0) { + memcpy((void *)DEBUGFS_SHARED_BUF_VIRT, &parms, + sizeof(union debugfs_parms)); + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + /* Not implemented */ + case CREATE: + /* Intentional fall-through */ + + /* Not implemented */ + case WRITE: + /* Intentional fall-through */ + + default: + smc_ret = SMC_UNK; + smc_resp = 0; + } + + spin_unlock(&debugfs_access_lock); + + SMC_RET2(handle, smc_ret, smc_resp); + + /* Not reached */ + return smc_ret; +} + +int debugfs_smc_setup(void) +{ + debugfs_initialized = false; + debugfs_access_lock.lock = 0; + + return 0; +} 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(); +} diff --git a/lib/debugfs/dev.h b/lib/debugfs/dev.h new file mode 100644 index 0000000..c142651 --- /dev/null +++ b/lib/debugfs/dev.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DEV_H +#define DEV_H + +#include <cdefs.h> +#include <lib/debugfs.h> +#include <stddef.h> + +/* FIXME: need configurability */ +#define NR_CHANS 10 +#define NR_CONSS 1 +#define NR_BINDS 4 +#define NR_FILES 18 + +#define NODEV 255 +#define CHDIR (1 << 15) + +#define SYNCDEV 0 +#define SYNCALL 1 + +typedef struct dev dev_t; +typedef struct chan chan_t; +typedef struct dirtab dirtab_t; +typedef int devgen_t(chan_t *, const dirtab_t *, int, int, dir_t *); +typedef struct attr attr_t; + +enum { + DEV_ROOT_QROOT, + DEV_ROOT_QDEV, + DEV_ROOT_QFIP, + DEV_ROOT_QBLOBS, + DEV_ROOT_QBLOBCTL, + DEV_ROOT_QPSCI +}; + +/******************************************************************************* + * This structure contains the necessary information to represent a directory + * of the filesystem. + ******************************************************************************/ +struct dirtab { + char name[NAMELEN]; + qid_t qid; + long length; + unsigned char perm; + void *data; +}; + +/******************************************************************************* + * This structure defines the interface of device drivers. + * Each driver must implement a subset of those functions. + * It is possible to redirect to default implementations defined in dev.c. + ******************************************************************************/ +/* FIXME: comments for the callbacks */ +struct dev { + char id; + int (*stat)(chan_t *c, const char *file, dir_t *dir); + int (*walk)(chan_t *c, const char *name); + int (*read)(chan_t *c, void *buf, int n); + int (*write)(chan_t *c, void *buf, int n); + int (*seek)(chan_t *c, long off, int whence); + chan_t *(*clone)(chan_t *c, chan_t *nc); + chan_t *(*attach)(int id, int dev); + chan_t *(*mount)(chan_t *c, const char *spec); +}; + +/******************************************************************************* + * This structure defines the channel structure. + * A channel is a handle on an element of the filesystem. + ******************************************************************************/ +struct chan { + long offset; + qid_t qid; + unsigned char index; /* device index in devtab */ + unsigned char dev; + unsigned char mode; +}; + +/******************************************************************************* + * This structure defines an abstract argument passed to physical drivers from + * the configuration file. + ******************************************************************************/ +struct attr { + char *key; + char *value; +}; + +chan_t *path_to_channel(const char *path, int mode); +chan_t *clone(chan_t *c, chan_t *nc); +chan_t *attach(int id, int dev); +void channel_close(chan_t *c); +int buf_to_channel(chan_t *c, void *dst, void *src, int nbytes, long len); +int dirread(chan_t *c, dir_t *dir, const dirtab_t *tab, + int ntab, devgen_t *gen); +void make_dir_entry(chan_t *c, dir_t *dir, const char *name, long length, + qid_t qid, unsigned int mode); +void devlink(void); + +chan_t *devattach(int id, int dev); +int devseek(chan_t *c, long off, int whence); +chan_t *devclone(chan_t *c, chan_t *nc); +int devgen(chan_t *c, const dirtab_t *tab, int ntab, int n, dir_t *dir); +int devwalk(chan_t *c, const char *name, const dirtab_t *tab, int ntab, + devgen_t *gen); +int devstat(chan_t *dirc, const char *file, dir_t *dir, + const dirtab_t *tab, int ntab, devgen_t *gen); + +chan_t *deverrmount(chan_t *c, const char *spec); +int deverrwrite(chan_t *c, void *buf, int n); +int deverrseek(chan_t *c, long off, int whence); + +extern dev_t *const devtab[]; + +void __dead2 devpanic(const char *cause); + +#endif /* DEV_H */ diff --git a/lib/debugfs/devc.c b/lib/debugfs/devc.c new file mode 100644 index 0000000..1099a85 --- /dev/null +++ b/lib/debugfs/devc.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +typedef struct dev dev_t; + +extern dev_t rootdevtab; +extern dev_t fipdevtab; + +dev_t *const devtab[] = { + &rootdevtab, + &fipdevtab, + 0 +}; + +void devlink(void) +{ +} diff --git a/lib/debugfs/devfip.c b/lib/debugfs/devfip.c new file mode 100644 index 0000000..85e6403 --- /dev/null +++ b/lib/debugfs/devfip.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2019-2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <lib/debugfs.h> +#include <limits.h> +#include <plat/arm/common/plat_arm.h> +#include <stdlib.h> +#include <string.h> +#include <tools_share/firmware_image_package.h> + +#include "dev.h" + +#define NR_FIPS 1 +#define STOC_HEADER (sizeof(fip_toc_header_t)) +#define STOC_ENTRY (sizeof(fip_toc_entry_t)) + +struct fipfile { + chan_t *c; + long offset[NR_FILES]; + long size[NR_FILES]; +}; + +struct fip_entry { + uuid_t uuid; + long long offset_address; + long long size; + long long flags; +}; + +struct uuidnames { + const char name[NAMELEN]; + const uuid_t uuid; +}; + +/******************************************************************************* + * This array links the FIP file names to their UUID. + * The elements are ordered according to the image number stored in + * tbbr_img_def.h, starting at index 1. + * + * TODO: this name to uuid binding will preferably be done using + * the coming Property Access Layer / Firmware CONFiguration feature. + ******************************************************************************/ +static const struct uuidnames uuidnames[] = { + {"", { {0}, {0}, {0}, 0, 0, {0} } }, + {"bl2.bin", UUID_TRUSTED_BOOT_FIRMWARE_BL2}, + {"scp-bl2.bin", UUID_SCP_FIRMWARE_SCP_BL2}, + {"bl31.bin", UUID_EL3_RUNTIME_FIRMWARE_BL31}, + {"bl32.bin", UUID_SECURE_PAYLOAD_BL32}, + {"bl33.bin", UUID_NON_TRUSTED_FIRMWARE_BL33}, + {"tb-fw.crt", UUID_TRUSTED_BOOT_FW_CERT}, + {"trstd-k.crt", UUID_TRUSTED_KEY_CERT}, + {"scp-fw-k.crt", UUID_SCP_FW_KEY_CERT}, + {"soc-fw-k.crt", UUID_SOC_FW_KEY_CERT}, + {"tos-fw-k.crt", UUID_TRUSTED_OS_FW_KEY_CERT}, + {"nt-fw-k.crt", UUID_NON_TRUSTED_FW_KEY_CERT}, + {"scp-fw-c.crt", UUID_SCP_FW_CONTENT_CERT}, + {"soc-fw-c.crt", UUID_SOC_FW_CONTENT_CERT}, + {"tos-fw-c.crt", UUID_TRUSTED_OS_FW_CONTENT_CERT}, + {"nt-fw-c.crt", UUID_NON_TRUSTED_FW_CONTENT_CERT}, + { }, + {"fwu.crt", UUID_TRUSTED_FWU_CERT}, + {"scp-bl2u.bin", UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U}, + {"bl2u.bin", UUID_TRUSTED_UPDATE_FIRMWARE_BL2U}, + {"ns-bl2u.bin", UUID_TRUSTED_UPDATE_FIRMWARE_NS_BL2U}, + {"bl32-xtr1.bin", UUID_SECURE_PAYLOAD_BL32_EXTRA1}, + {"bl32-xtr2.bin", UUID_SECURE_PAYLOAD_BL32_EXTRA2}, + {"hw.cfg", UUID_HW_CONFIG}, + {"tb-fw.cfg", UUID_TB_FW_CONFIG}, + {"soc-fw.cfg", UUID_SOC_FW_CONFIG}, + {"tos-fw.cfg", UUID_TOS_FW_CONFIG}, + {"nt-fw.cfg", UUID_NT_FW_CONFIG}, + {"fw.cfg", UUID_FW_CONFIG}, + {"rot-k.crt", UUID_ROT_KEY_CERT}, + {"nt-k.crt", UUID_NON_TRUSTED_WORLD_KEY_CERT}, + {"sip-sp.crt", UUID_SIP_SECURE_PARTITION_CONTENT_CERT}, + {"plat-sp.crt", UUID_PLAT_SECURE_PARTITION_CONTENT_CERT} +}; + +/******************************************************************************* + * This array contains all the available FIP files. + ******************************************************************************/ +static struct fipfile archives[NR_FIPS]; + +/******************************************************************************* + * This variable stores the current number of registered FIP files. + ******************************************************************************/ +static int nfips; + +/******************************************************************************* + * This function parses the ToC of the FIP. + ******************************************************************************/ +static int get_entry(chan_t *c, struct fip_entry *entry) +{ + int n; + + n = devtab[c->index]->read(c, entry, sizeof(struct fip_entry)); + if (n <= 0) { + return n; + } + + if (n != sizeof(struct fip_entry)) { + return -1; + } + + if (entry->size == 0) { + return 0; + } + + return 1; +} + +/******************************************************************************* + * This function exposes the FIP images as files. + ******************************************************************************/ +static int fipgen(chan_t *c, const dirtab_t *tab, int ntab, int n, dir_t *dir) +{ + int i, r; + long off; + chan_t nc; + struct fip_entry entry; + struct fipfile *fip; + static const char unk[] = "unknown"; + + if (c->dev >= nfips) { + panic(); + } + + if (clone(archives[c->dev].c, &nc) == NULL) { + panic(); + } + + fip = &archives[nc.dev]; + + off = STOC_HEADER; + for (i = 0; i <= n; i++) { + if (fip->offset[i] == -1) { + return 0; + } + + if (devtab[nc.index]->seek(&nc, off, KSEEK_SET) < 0) { + return -1; + } + + r = get_entry(&nc, &entry); + if (r <= 0) { + return r; + } + + off += sizeof(entry); + } + + for (i = 1; i < NELEM(uuidnames); i++) { + if (memcmp(&uuidnames[i].uuid, + &entry.uuid, sizeof(uuid_t)) == 0) { + break; + } + } + + if (i < NELEM(uuidnames)) { + make_dir_entry(c, dir, uuidnames[i].name, + entry.size, n, O_READ); + } else { + // TODO: set name depending on uuid node value + make_dir_entry(c, dir, unk, entry.size, n, O_READ); + } + + return 1; +} + +static int fipwalk(chan_t *c, const char *name) +{ + return devwalk(c, name, NULL, 0, fipgen); +} + +static int fipstat(chan_t *c, const char *file, dir_t *dir) +{ + return devstat(c, file, dir, NULL, 0, fipgen); +} + +/******************************************************************************* + * This function copies at most n bytes of the FIP image referred by c into + * buf. + ******************************************************************************/ +static int fipread(chan_t *c, void *buf, int n) +{ + long off; + chan_t cs; + struct fipfile *fip; + long size; + + /* Only makes sense when using debug language */ + assert(c->qid != CHDIR); + + if ((c->dev >= nfips) || ((c->qid & CHDIR) != 0)) { + panic(); + } + + fip = &archives[c->dev]; + + if ((c->qid >= NR_FILES) || (fip->offset[c->qid] < 0)) { + panic(); + } + + if (clone(fip->c, &cs) == NULL) { + panic(); + } + + size = fip->size[c->qid]; + if (c->offset >= size) { + return 0; + } + + if (n < 0) { + return -1; + } + + if (n > (size - c->offset)) { + n = size - c->offset; + } + + off = fip->offset[c->qid] + c->offset; + if (devtab[cs.index]->seek(&cs, off, KSEEK_SET) < 0) { + return -1; + } + + n = devtab[cs.index]->read(&cs, buf, n); + if (n > 0) { + c->offset += n; + } + + return n; +} + +/******************************************************************************* + * This function parses the FIP spec and registers its images in order to + * expose them as files in the driver namespace. + * It acts as an initialization function for the FIP driver. + * It returns a pointer to the newly created channel. + ******************************************************************************/ +static chan_t *fipmount(chan_t *c, const char *spec) +{ + int r, n, t; + chan_t *cspec; + uint32_t hname; + struct fip_entry entry; + struct fipfile *fip; + dir_t dir; + + if (nfips == NR_FIPS) { + return NULL; + } + + fip = &archives[nfips]; + + for (n = 0; n < NR_FILES; n++) { + fip->offset[n] = -1; + } + + cspec = path_to_channel(spec, O_READ); + if (cspec == NULL) { + return NULL; + } + + fip->c = cspec; + + r = devtab[cspec->index]->read(cspec, &hname, sizeof(hname)); + if (r < 0) { + goto err; + } + + if ((r != sizeof(hname)) || (hname != TOC_HEADER_NAME)) { + goto err; + } + + if (stat(spec, &dir) < 0) { + goto err; + } + + t = cspec->index; + if (devtab[t]->seek(cspec, STOC_HEADER, KSEEK_SET) < 0) { + goto err; + } + + for (n = 0; n < NR_FILES; n++) { + switch (get_entry(cspec, &entry)) { + case 0: + return attach('F', nfips++); + case -1: + goto err; + default: + if ((entry.offset_address + entry.size) > dir.length) { + goto err; + } + + fip->offset[n] = entry.offset_address; + fip->size[n] = entry.size; + break; + } + } + +err: + channel_close(cspec); + return NULL; +} + +const dev_t fipdevtab = { + .id = 'F', + .stat = fipstat, + .clone = devclone, + .attach = devattach, + .walk = fipwalk, + .read = fipread, + .write = deverrwrite, + .mount = fipmount, + .seek = devseek +}; + diff --git a/lib/debugfs/devroot.c b/lib/debugfs/devroot.c new file mode 100644 index 0000000..9dd6c92 --- /dev/null +++ b/lib/debugfs/devroot.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <common/debug.h> +#include <lib/debugfs.h> + +#include "blobs.h" +#include "dev.h" + +/******************************************************************************* + * This array contains the directories available from the root directory. + ******************************************************************************/ +static const dirtab_t dirtab[] = { + {"dev", CHDIR | DEV_ROOT_QDEV, 0, O_READ}, + {"blobs", CHDIR | DEV_ROOT_QBLOBS, 0, O_READ}, + {"fip", CHDIR | DEV_ROOT_QFIP, 0, O_READ} +}; + +static const dirtab_t devfstab[] = { +}; + +/******************************************************************************* + * This function exposes the elements of the root directory. + * It also exposes the content of the dev and blobs directories. + ******************************************************************************/ +static int rootgen(chan_t *channel, const dirtab_t *tab, int ntab, + int n, dir_t *dir) +{ + switch (channel->qid & ~CHDIR) { + case DEV_ROOT_QROOT: + tab = dirtab; + ntab = NELEM(dirtab); + break; + case DEV_ROOT_QDEV: + tab = devfstab; + ntab = NELEM(devfstab); + break; + case DEV_ROOT_QBLOBS: + tab = blobtab; + ntab = NELEM(blobtab); + break; + default: + return 0; + } + + return devgen(channel, tab, ntab, n, dir); +} + +static int rootwalk(chan_t *channel, const char *name) +{ + return devwalk(channel, name, NULL, 0, rootgen); +} + +/******************************************************************************* + * This function copies at most n bytes from the element referred by c into buf. + ******************************************************************************/ +static int rootread(chan_t *channel, void *buf, int size) +{ + const dirtab_t *dp; + dir_t *dir; + + if ((channel->qid & CHDIR) != 0) { + if (size < sizeof(dir_t)) { + return -1; + } + + dir = buf; + return dirread(channel, dir, NULL, 0, rootgen); + } + + /* Only makes sense when using debug language */ + assert(channel->qid != DEV_ROOT_QBLOBCTL); + + dp = &blobtab[channel->qid - DEV_ROOT_QBLOBCTL]; + return buf_to_channel(channel, buf, dp->data, size, dp->length); +} + +static int rootstat(chan_t *channel, const char *file, dir_t *dir) +{ + return devstat(channel, file, dir, NULL, 0, rootgen); +} + +const dev_t rootdevtab = { + .id = '/', + .stat = rootstat, + .clone = devclone, + .attach = devattach, + .walk = rootwalk, + .read = rootread, + .write = deverrwrite, + .mount = deverrmount, + .seek = devseek +}; |