diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/dtm/Makefile | 15 | ||||
| -rw-r--r-- | kernel/dtm/README.md | 29 | ||||
| -rw-r--r-- | kernel/dtm/dtm.c | 114 |
3 files changed, 158 insertions, 0 deletions
diff --git a/kernel/dtm/Makefile b/kernel/dtm/Makefile new file mode 100644 index 0000000..5233661 --- /dev/null +++ b/kernel/dtm/Makefile @@ -0,0 +1,15 @@ +obj-m += dtm.o + +# Build either from a local kernel or from the running system. +KERNEL ?= +ifeq ($(strip $(KERNEL)),) + DIR=/lib/modules/$$(uname -r)/build +else + DIR="$(KERNEL)" +endif + +all: + make -C $(DIR) M=$(PWD) modules + +clean: + make -C $(DIR) M=$(PWD) clean diff --git a/kernel/dtm/README.md b/kernel/dtm/README.md new file mode 100644 index 0000000..593c1d2 --- /dev/null +++ b/kernel/dtm/README.md @@ -0,0 +1,29 @@ +# Device Tree Module + +This is a simple kernel module which show cases some of the kernel API functions +for manipulating device tree nodes. + +In order to build this module you need either a local build of the Linux kernel, +or the the source that you can get from your Linux distribution (e.g. +`kernel-source` on openSUSE). For the former you will need to specify the path +to your local development tree with the `KERNEL` variable. So: + +``` +$ make KERNEL=<path/to/development/tree> +``` + +This will produce a `dtm.ko` file, which you will need to `insmod` on your +target machine. Then you will get this output from `dmesg`: + +``` +[ 571.298548] [ T813] dtm: loading out-of-tree module taints kernel. +[ 571.305842] [ T813] Inside of VisionFive2 +[ 571.309945] [ T813] RISC-V ISA for 'cpu@1': rv64imafdc_zba_zbb +[ 571.315826] [ T813] parent: cpus +[ 571.319091] [ T813] RISC-V ISA for 'cpu@2': rv64imafdc_zba_zbb +[ 571.324962] [ T813] parent: cpus +[ 571.328224] [ T813] RISC-V ISA for 'cpu@3': rv64imafdc_zba_zbb +[ 571.334092] [ T813] parent: cpus +[ 571.337354] [ T813] RISC-V ISA for 'cpu@4': rv64imafdc_zba_zbb +[ 571.343228] [ T813] parent: cpus +``` diff --git a/kernel/dtm/dtm.c b/kernel/dtm/dtm.c new file mode 100644 index 0000000..adda89a --- /dev/null +++ b/kernel/dtm/dtm.c @@ -0,0 +1,114 @@ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> + +MODULE_DESCRIPTION("Kernel module to show off the DeviceTree Kernel API"); +MODULE_AUTHOR("Miquel Sabaté Solà"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.0.1"); + +// `compatible` value for QEMU/KVM. +static const char *const qemu[] = { + "riscv-virtio", + NULL, +}; + +// `compatible` values that will match on Starfive VisionFive2 v1.3b (plus a +// boogie value). +static const char *const vf2[] = { + "starfive,visionfive-2-v1.3b", + "starfive,jh7110", + "this is bullshit but it won't interfere", + NULL, +}; + +static int __init dtm_init(void) +{ + unsigned int cpu; + struct device_node *node, *parent; + const char *isa; + int rc; + + /* + * `of_compatible_match` returns true if there is _one_ element of the given + * array which matches a string on the `compatible` file. That is, not all + * of them have to match. + */ + if (of_machine_compatible_match(qemu)) { + pr_info("Inside of QEMU\n"); + } else if (of_machine_compatible_match(vf2)) { + pr_info("Inside of VisionFive2\n"); + } else { + pr_info("Somewhere over the rainbow\n"); + } + + /* + * The `for_each_possible_cpu` is a function-like macro provided by the + * Kernel which allows `cpu` to be an iterator over CPU device nodes. + */ + for_each_possible_cpu(cpu) { + /* isa = NULL; */ + + /* + * Given a CPU identifier, `of_cpu_device_node_get` returns a `struct + * device_node` related to the specified CPU (or NULL on error). + */ + node = of_cpu_device_node_get(cpu); + if (!node) { + pr_warn("Unable to find cpu node\n"); + continue; + } + + /* + * `of_property_present` returns a boolean on whether the given node + * property is present for the given device node. + */ + if (!of_property_present(node, "riscv,isa")) { + pr_warn("Unable to find 'riscv,isa' for this node\n"); + continue; + } + + /* + * `of_property_read_string` initializes the given `isa` string with the + * contents of the property we are trying to fetch. Returns 0 on + * success, and a kernel error otherwise. Note that the previous + * `of_property_present` call was actually not needed, we would've get + * here an `-EINVAL` error eitherway. + */ + rc = of_property_read_string(node, "riscv,isa", &isa); + if (rc) { + pr_warn("Unable to find \"riscv,isa\" devicetree entry\n"); + continue; + } + pr_info("RISC-V ISA for '%s': %s\n", node->full_name, isa); + + /* + * `of_get_parent` returns the parent device node for the one provided + * or NULL if there's none. + */ + parent = of_get_parent(node); + if (!node) { + pr_warn("No parent for the given node!\n"); + continue; + } + pr_info("parent: %s\n", node->full_name); + + /* + * `of_node_put` is used to decrement the reference counter on device + * nodes. We are done using these instances, so we should decrement + * their life times. + */ + of_node_put(node); + of_node_put(parent); + } + return 0; +} + +static void __exit dtm_exit(void) +{ + pr_info("Exiting from DTM module\n"); +} + +module_init(dtm_init); +module_exit(dtm_exit); |
