From 46b0f6d6eefcbeaf66c30c4ae3e86735d234b532 Mon Sep 17 00:00:00 2001 From: Brennan Ashton Date: Sun, 7 May 2023 00:13:31 -0700 Subject: [PATCH] fdt: Add initial FDT support and procfs for userspace export VELAPLATFO-12536 This provides the initial hooks for Flattened Device Tree support with QEMU RV. It also provides a new procfs file that exposes the fdt to userspace much like the /sys/firmware/fdt endpoint in Linux. See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-firmware-ofw Nodes in the fdt are not yet usable by the OS. Signed-off-by: Brennan Ashton Signed-off-by: liaoao --- arch/risc-v/src/qemu-rv/qemu_rv_head.S | 1 + arch/risc-v/src/qemu-rv/qemu_rv_start.c | 19 +- .../qemu-rv/rv-virt/configs/nsh64/defconfig | 1 + drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/devicetree/CMakeLists.txt | 24 ++ drivers/devicetree/Kconfig | 10 + drivers/devicetree/Make.defs | 28 ++ drivers/devicetree/fdt.c | 86 ++++++ fs/procfs/Kconfig | 8 + fs/procfs/Make.defs | 6 +- fs/procfs/fs_procfs.c | 5 + fs/procfs/fs_procfsfdt.c | 258 ++++++++++++++++++ include/nuttx/fdt.h | 88 ++++++ 14 files changed, 528 insertions(+), 8 deletions(-) create mode 100644 drivers/devicetree/CMakeLists.txt create mode 100644 drivers/devicetree/Kconfig create mode 100644 drivers/devicetree/Make.defs create mode 100644 drivers/devicetree/fdt.c create mode 100644 fs/procfs/fs_procfsfdt.c create mode 100644 include/nuttx/fdt.h diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_head.S b/arch/risc-v/src/qemu-rv/qemu_rv_head.S index 4cce5e1ed25..9e321bb62a6 100644 --- a/arch/risc-v/src/qemu-rv/qemu_rv_head.S +++ b/arch/risc-v/src/qemu-rv/qemu_rv_head.S @@ -41,6 +41,7 @@ __start: + /* Preserve a1 as it contains the pointer to DTB */ /* Load mhartid (cpuid) */ csrr a0, mhartid diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_start.c b/arch/risc-v/src/qemu-rv/qemu_rv_start.c index 303fe217213..e3465bcac53 100644 --- a/arch/risc-v/src/qemu-rv/qemu_rv_start.c +++ b/arch/risc-v/src/qemu-rv/qemu_rv_start.c @@ -36,6 +36,10 @@ # include "qemu_rv_mm_init.h" #endif +#ifdef CONFIG_DEVICE_TREE +# include +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -97,9 +101,9 @@ uintptr_t g_idle_topstack = QEMU_RV_IDLESTACK_TOP; ****************************************************************************/ #ifdef CONFIG_BUILD_KERNEL -void qemu_rv_start_s(int mhartid) +void qemu_rv_start_s(int mhartid, const char *dtb) #else -void qemu_rv_start(int mhartid) +void qemu_rv_start(int mhartid, const char *dtb) #endif { /* Configure FPU */ @@ -115,6 +119,10 @@ void qemu_rv_start(int mhartid) qemu_rv_clear_bss(); #endif +#ifdef CONFIG_DEVICE_TREE + fdt_register(dtb); +#endif + showprogress('A'); #ifdef USE_EARLYSERIALINIT @@ -155,7 +163,7 @@ cpux: * Name: qemu_rv_start ****************************************************************************/ -void qemu_rv_start(int mhartid) +void qemu_rv_start(int mhartid, const char *dtb) { /* NOTE: still in M-mode */ @@ -209,12 +217,13 @@ void qemu_rv_start(int mhartid) WRITE_CSR(mepc, (uintptr_t)qemu_rv_start_s); - /* Set a0 to mhartid explicitly and enter to S-mode */ + /* Set a0 to mhartid and a1 to dtb explicitly and enter to S-mode */ asm volatile ( "mv a0, %0 \n" + "mv a1, %1 \n" "mret \n" - :: "r" (mhartid) + :: "r" (mhartid), "r" (dtb) ); } #endif diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/nsh64/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/nsh64/defconfig index e7870d9c94b..37cd640d5fe 100644 --- a/boards/risc-v/qemu-rv/rv-virt/configs/nsh64/defconfig +++ b/boards/risc-v/qemu-rv/rv-virt/configs/nsh64/defconfig @@ -31,6 +31,7 @@ CONFIG_BOARD_LOOPSPERMSEC=6366 CONFIG_BUILTIN=y CONFIG_DEBUG_FULLOPT=y CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEVICE_TREE=y CONFIG_DEV_ZERO=y CONFIG_ELF=y CONFIG_EXAMPLES_HELLO=m diff --git a/drivers/Kconfig b/drivers/Kconfig index 50487c6c515..2a90acbb02d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -54,3 +54,4 @@ source "drivers/math/Kconfig" source "drivers/segger/Kconfig" source "drivers/usrsock/Kconfig" source "drivers/dma/Kconfig" +source "drivers/devicetree/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index b2dea190483..0bb262423f8 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -32,6 +32,7 @@ include bch/Make.defs include can/Make.defs include clk/Make.defs include crypto/Make.defs +include devicetree/Make.defs include dma/Make.defs include math/Make.defs include motor/Make.defs diff --git a/drivers/devicetree/CMakeLists.txt b/drivers/devicetree/CMakeLists.txt new file mode 100644 index 00000000000..465401bc613 --- /dev/null +++ b/drivers/devicetree/CMakeLists.txt @@ -0,0 +1,24 @@ +# ############################################################################## +# drivers/devicetree/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## +if(CONFIG_DEVICE_TREE) + set(SRCS fdt.c) + + target_sources(drivers PRIVATE ${SRCS}) +endif() diff --git a/drivers/devicetree/Kconfig b/drivers/devicetree/Kconfig new file mode 100644 index 00000000000..123dcb5f001 --- /dev/null +++ b/drivers/devicetree/Kconfig @@ -0,0 +1,10 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig DEVICE_TREE + bool "Device Tree Support" + default n + ---help--- + Interface for interacting with devicetree. diff --git a/drivers/devicetree/Make.defs b/drivers/devicetree/Make.defs new file mode 100644 index 00000000000..956de78a0d4 --- /dev/null +++ b/drivers/devicetree/Make.defs @@ -0,0 +1,28 @@ +############################################################################ +# drivers/devicetree/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifeq ($(CONFIG_DEVICE_TREE),y) + +CSRCS += fdt.c + +DEPPATH += --dep-path devicetree +VPATH += :devicetree + +endif diff --git a/drivers/devicetree/fdt.c b/drivers/devicetree/fdt.c new file mode 100644 index 00000000000..f07f23357df --- /dev/null +++ b/drivers/devicetree/fdt.c @@ -0,0 +1,86 @@ +/**************************************************************************** + * drivers/devicetree/fdt.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Location of the fdt data for this system. */ + +static FAR const char *g_fdt_base = NULL; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fdt_register + * + * Description: + * Store the pointer to the flattened device tree and verify that it at + * least appears to be valid. This function will not fully parse the FDT. + * + * Return: + * Return -EINVAL if the fdt header does not have the expected magic value. + * otherwise return OK. If OK is not returned the existing entry for FDT + * is not modified. + * + ****************************************************************************/ + +int fdt_register(FAR const char *fdt_base) +{ + struct fdt_header_s *fdt_header; + + DEBUGASSERT(fdt_base); + + fdt_header = (struct fdt_header_s *)fdt_base; + if (fdt_header->magic != be32toh(FDT_MAGIC)) + { + return -EINVAL; /* Bad magic byte read */ + } + + g_fdt_base = fdt_base; + return OK; +} + +/**************************************************************************** + * Name: fdt_get + * + * Description: + * Return the pointer to a raw FDT. NULL is returned if no FDT has been + * loaded. + * + ****************************************************************************/ + +FAR const char *fdt_get(void) +{ + return g_fdt_base; +} diff --git a/fs/procfs/Kconfig b/fs/procfs/Kconfig index 66918989447..d6113ded3d9 100644 --- a/fs/procfs/Kconfig +++ b/fs/procfs/Kconfig @@ -56,6 +56,14 @@ config FS_PROCFS_EXCLUDE_ENVIRON Causes the environment variable information to be excluded from the procfs system. This will reduce code space slightly. +config FS_PROCFS_EXCLUDE_FDT + bool "Exclude flattened device tree blob" + depends on DEVICE_TREE + default DEFAULT_SMALL + ---help--- + Causes the flatted device tree information to be excluded from the + procfs system. This will reduce code space slightly. + config FS_PROCFS_EXCLUDE_IOBINFO bool "Exclude iobinfo" depends on MM_IOB diff --git a/fs/procfs/Make.defs b/fs/procfs/Make.defs index 57dbf97d06f..318cca570e3 100644 --- a/fs/procfs/Make.defs +++ b/fs/procfs/Make.defs @@ -22,9 +22,9 @@ ifeq ($(CONFIG_FS_PROCFS),y) # Files required for procfs file system support CSRCS += fs_procfs.c fs_procfscpuinfo.c fs_procfscpuload.c -CSRCS += fs_procfscritmon.c fs_procfsiobinfo.c fs_procfsmeminfo.c -CSRCS += fs_procfsproc.c fs_procfstcbinfo.c fs_procfsuptime.c -CSRCS += fs_procfsutil.c fs_procfsversion.c +CSRCS += fs_procfscritmon.c fs_procfsfdt.c fs_procfsiobinfo.c +CSRCS += fs_procfsmeminfo.c fs_procfsproc.c fs_procfstcbinfo.c +CSRCS += fs_procfsuptime.c fs_procfsutil.c fs_procfsversion.c # Include procfs build support diff --git a/fs/procfs/fs_procfs.c b/fs/procfs/fs_procfs.c index c8fc8ba0d72..4d5af33ee28 100644 --- a/fs/procfs/fs_procfs.c +++ b/fs/procfs/fs_procfs.c @@ -55,6 +55,7 @@ extern const struct procfs_operations g_cpuinfo_operations; extern const struct procfs_operations g_cpuload_operations; extern const struct procfs_operations g_critmon_operations; +extern const struct procfs_operations g_fdt_operations; extern const struct procfs_operations g_iobinfo_operations; extern const struct procfs_operations g_irq_operations; extern const struct procfs_operations g_meminfo_operations; @@ -108,6 +109,10 @@ static const struct procfs_entry_s g_procfs_entries[] = { "critmon", &g_critmon_operations, PROCFS_FILE_TYPE }, #endif +#if defined(CONFIG_DEVICE_TREE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_FDT) + { "fdt", &g_fdt_operations, PROCFS_FILE_TYPE }, +#endif + #ifndef CONFIG_FS_PROCFS_EXCLUDE_BLOCKS { "fs/blocks", &g_mount_operations, PROCFS_FILE_TYPE }, #endif diff --git a/fs/procfs/fs_procfsfdt.c b/fs/procfs/fs_procfsfdt.c new file mode 100644 index 00000000000..a30fc61d2b3 --- /dev/null +++ b/fs/procfs/fs_procfsfdt.c @@ -0,0 +1,258 @@ +/**************************************************************************** + * fs/procfs/fs_procfsfdt.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_DEVICE_TREE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_FDT) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes one open "file" */ + +struct fdt_file_s +{ + struct procfs_file_s base; /* Base open file structure */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* File system methods */ + +static int fdt_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); +static int fdt_close(FAR struct file *filep); +static ssize_t fdt_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static int fdt_dup(FAR const struct file *oldp, + FAR struct file *newp); +static int fdt_stat(FAR const char *relpath, FAR struct stat *buf); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* See fs_mount.c -- this structure is explicitly externed there. + * We use the old-fashioned kind of initializers so that this will compile + * with any compiler. + */ + +const struct procfs_operations g_fdt_operations = +{ + fdt_open, /* open */ + fdt_close, /* close */ + fdt_read, /* read */ + NULL, /* write */ + fdt_dup, /* dup */ + NULL, /* opendir */ + NULL, /* closedir */ + NULL, /* readdir */ + NULL, /* rewinddir */ + fdt_stat /* stat */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fdt_open + ****************************************************************************/ + +static int fdt_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct fdt_file_s *attr; + + finfo("Open '%s'\n", relpath); + + /* PROCFS is read-only. Any attempt to open with any kind of write + * access is not permitted. + * + * REVISIT: Write-able proc files could be quite useful. + */ + + if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) + { + ferr("ERROR: Only O_RDONLY supported\n"); + return -EACCES; + } + + /* Allocate a container to hold the file attributes */ + + attr = kmm_zalloc(sizeof(struct fdt_file_s)); + if (attr == NULL) + { + ferr("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* Save the attributes as the open-specific state in filep->f_priv */ + + filep->f_priv = attr; + return OK; +} + +/**************************************************************************** + * Name: fdt_close + ****************************************************************************/ + +static int fdt_close(FAR struct file *filep) +{ + FAR struct fdt_file_s *attr; + + /* Recover our private data from the struct file instance */ + + attr = filep->f_priv; + DEBUGASSERT(attr); + + /* Release the file attributes structure */ + + kmm_free(attr); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: fdt_read + ****************************************************************************/ + +static ssize_t fdt_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR const char *fdt; + FAR struct fdt_header_s *fdt_header; + off_t offset; + ssize_t ret; + + finfo("buffer=%p buflen=%zu\n", buffer, buflen); + DEBUGASSERT(filep != NULL && buffer != NULL && buflen > 0); + + /* Load FDT and parse extents. */ + + fdt = fdt_get(); + if (fdt == NULL) + { + ferr("FDT cannot be read.\n"); + return -ENOENT; + } + + /* Transfer the fdt to user receive buffer */ + + fdt_header = (struct fdt_header_s *)fdt; + offset = filep->f_pos; + ret = procfs_memcpy(fdt, be32toh(fdt_header->totalsize), + buffer, buflen, &offset); + + /* Update the file offset */ + + if (ret > 0) + { + filep->f_pos += ret; + } + + return ret; +} + +/**************************************************************************** + * Name: fdt_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int fdt_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct fdt_file_s *oldattr; + FAR struct fdt_file_s *newattr; + + finfo("Dup %p->%p\n", oldp, newp); + + /* Recover our private data from the old struct file instance */ + + oldattr = oldp->f_priv; + DEBUGASSERT(oldattr); + + /* Allocate a new container to hold the task and attribute selection */ + + newattr = kmm_malloc(sizeof(struct fdt_file_s)); + if (newattr == NULL) + { + ferr("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* The copy the file attributes from the old attributes to the new */ + + memcpy(newattr, oldattr, sizeof(struct fdt_file_s)); + + /* Save the new attributes in the new file structure */ + + newp->f_priv = newattr; + return OK; +} + +/**************************************************************************** + * Name: fdt_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int fdt_stat(FAR const char *relpath, FAR struct stat *buf) +{ + /* "fdt" is the name for a read-only file */ + + memset(buf, 0, sizeof(struct stat)); + buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#endif /* CONFIG_DEVICE_TREE && CONFIG_FS_PROCFS_EXCLUDE_FDT */ diff --git a/include/nuttx/fdt.h b/include/nuttx/fdt.h new file mode 100644 index 00000000000..4e432a13941 --- /dev/null +++ b/include/nuttx/fdt.h @@ -0,0 +1,88 @@ +/**************************************************************************** + * include/nuttx/fdt.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_FDT_H +#define __INCLUDE_NUTTX_FDT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FDT_MAGIC 0xd00dfeed + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct fdt_header_s +{ + uint32_t magic; + uint32_t totalsize; + uint32_t off_dt_struct; + uint32_t off_dt_strings; + uint32_t off_mem_rsvmap; + uint32_t version; + uint32_t last_comp_version; + uint32_t boot_cpuid_phys; + uint32_t size_dt_strings; + uint32_t size_dt_struct; +}; + +/**************************************************************************** + * Public Functions Definitions + ****************************************************************************/ + +/**************************************************************************** + * Name: fdt_register + * + * Description: + * Store the pointer to the flattened device tree and verify that it at + * least appears to be valid. This function will not fully parse the FDT. + * + * Return: + * Return -EINVAL if the fdt header does not have the expected magic value. + * otherwise return OK. If OK is not returned the existing entry for FDT + * is not modified. + * + ****************************************************************************/ + +int fdt_register(FAR const char *fdt_base); + +/**************************************************************************** + * Name: fdt_get + * + * Description: + * Return the pointer to a raw FDT. NULL is returned if no FDT has been + * loaded. + * + ****************************************************************************/ + +FAR const char *fdt_get(void); + +#endif /* __INCLUDE_NUTTX_FDT_H */