summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kconfig12
-rw-r--r--common/board_f.c11
-rw-r--r--drivers/core/of_access.c52
-rw-r--r--drivers/core/ofnode.c48
-rw-r--r--include/asm-generic/sections.h19
-rw-r--r--include/dm/of_access.h20
-rw-r--r--include/dm/ofnode.h50
-rw-r--r--include/fdtdec.h16
-rw-r--r--include/linux/compat.h13
-rw-r--r--include/linux/device.h29
-rw-r--r--include/linux/err.h25
-rw-r--r--include/linux/kref.h124
-rw-r--r--include/linux/math64.h19
l---------include/linux/regmap.h1
-rw-r--r--include/linux/string.h2
-rw-r--r--include/power/regulator.h3
-rw-r--r--include/regmap.h9
-rw-r--r--lib/fdtdec.c18
-rw-r--r--lib/string.c31
-rw-r--r--lib/vsprintf.c37
20 files changed, 538 insertions, 1 deletions
diff --git a/Kconfig b/Kconfig
index 7f00e76ba78..d76db700577 100644
--- a/Kconfig
+++ b/Kconfig
@@ -468,6 +468,18 @@ config TOOLS_DEBUG
it is possible to set breakpoints on particular lines, single-step
debug through the source code, etc.
+config SKIP_EARLY_DM
+ bool "Skips initialising device model pre-relocation"
+ help
+ Enable this option to skip scanning and probing devices prior to
+ U-Boot relocation (during board_f). Unless console support is disabled
+ a serial port is still required, however this can be found through
+ /chosen/stdout-path in FDT. If the serial port relies on other devices
+ like clocks these will also be bound and probed on demand.
+
+ This can speed up time to interactive console by about 50%, particularly
+ when combined with OF_LIVE.
+
config SKIP_RELOCATE
bool "Skips relocation of U-Boot to end of RAM"
help
diff --git a/common/board_f.c b/common/board_f.c
index 11ad5779115..ce87c619e68 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -813,7 +813,16 @@ static int initf_dm(void)
return 0;
bootstage_start(BOOTSTAGE_ID_ACCUM_DM_F, "dm_f");
- ret = dm_init_and_scan(true);
+
+ /*
+ * If SKIP_EARLY_DM is set then we just create an empty device
+ * model, the serial port will still be bound later through
+ * serial_find_console_or_panic() via /chosen/stdout-path
+ */
+ if (!CONFIG_IS_ENABLED(SKIP_EARLY_DM))
+ ret = dm_init_and_scan(true);
+ else
+ ret = dm_init(false);
if (ret)
return ret;
diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c
index b11e36202c1..969492aae37 100644
--- a/drivers/core/of_access.c
+++ b/drivers/core/of_access.c
@@ -598,6 +598,25 @@ int of_read_u64(const struct device_node *np, const char *propname, u64 *outp)
return of_read_u64_index(np, propname, 0, outp);
}
+int of_read_u64_array(const struct device_node *np, const char *propname,
+ u64 *out_values, size_t sz)
+{
+ const __be64 *val;
+
+ log_debug("%s: %s: ", __func__, propname);
+ val = of_find_property_value_of_size(np, propname,
+ sz * sizeof(*out_values));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ log_debug("size %zd\n", sz);
+ while (sz--)
+ *out_values++ = be64_to_cpup(val++);
+
+ return 0;
+}
+
int of_property_match_string(const struct device_node *np, const char *propname,
const char *string)
{
@@ -845,6 +864,39 @@ int of_count_phandle_with_args(const struct device_node *np,
cell_count);
}
+/**
+ * of_property_count_elems_of_size - Count the number of elements in a property
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @elem_size: size of the individual element
+ *
+ * Search for a property in a device node and count the number of elements of
+ * size elem_size in it.
+ *
+ * Return: The number of elements on sucess, -EINVAL if the property does not
+ * exist or its length does not match a multiple of elem_size and -ENODATA if
+ * the property does not have a value.
+ */
+int of_property_count_elems_of_size(const struct device_node *np,
+ const char *propname, int elem_size)
+{
+ const struct property *prop = of_find_property(np, propname, NULL);
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ if (prop->length % elem_size != 0) {
+ pr_err("size of %s in node %pOF is not a multiple of %d\n",
+ propname, np, elem_size);
+ return -EINVAL;
+ }
+
+ return prop->length / elem_size;
+}
+
static void of_alias_add(struct alias_prop *ap, struct device_node *np,
int id, const char *stem, int stem_len)
{
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 3a36b6fdd03..d605c0f7b7c 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -664,6 +664,54 @@ int ofnode_read_u32_array(ofnode node, const char *propname,
}
}
+int ofnode_read_u64_array(ofnode node, const char *propname,
+ u64 *out_values, size_t sz)
+{
+ assert(ofnode_valid(node));
+ log_debug("%s: %s: ", __func__, propname);
+
+ if (ofnode_is_np(node)) {
+ return of_read_u64_array(ofnode_to_np(node), propname,
+ out_values, sz);
+ } else {
+ int ret;
+
+ ret = fdtdec_get_long_array(ofnode_to_fdt(node),
+ ofnode_to_offset(node), propname,
+ out_values, sz);
+
+ /* get the error right, but space is more important in SPL */
+ if (!IS_ENABLED(CONFIG_XPL_BUILD)) {
+ if (ret == -FDT_ERR_NOTFOUND)
+ return -EINVAL;
+ else if (ret == -FDT_ERR_BADLAYOUT)
+ return -EOVERFLOW;
+ }
+ return ret;
+ }
+}
+
+int ofnode_count_elems_of_size(ofnode node, const char *propname, int elem_size)
+{
+ const char *prop;
+ int len;
+ assert(ofnode_valid(node));
+
+ if (ofnode_is_np(node)) {
+ return of_property_count_elems_of_size(node.np, propname, elem_size);
+ } else {
+ prop = fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node), propname, &len);
+ if (!prop)
+ return -ENOENT;
+ if (len % elem_size != 0) {
+ log_debug("size of %s in node %pOF is not a multiple of %d\n",
+ propname, &node, elem_size);
+ return -EINVAL;
+ }
+ return len / elem_size;
+ }
+}
+
#if !CONFIG_IS_ENABLED(DM_INLINE_OFNODE)
bool ofnode_is_enabled(ofnode node)
{
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index d59787948fd..48bd4fa8604 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -9,6 +9,7 @@
#define _ASM_GENERIC_SECTIONS_H_
#include <linux/types.h>
+#include <stdbool.h>
/* References to section boundaries */
@@ -62,6 +63,24 @@ static inline int arch_is_kernel_data(unsigned long addr)
}
#endif
+/**
+ * is_kernel_rodata - checks if the pointer address is located in the
+ * .rodata section
+ *
+ * @addr: address to check
+ *
+ * Returns: true if the address is located in .rodata, false otherwise.
+ */
+static inline bool is_kernel_rodata(unsigned long addr)
+{
+#ifdef CONFIG_ARM64
+ return addr >= (unsigned long)__start_rodata &&
+ addr < (unsigned long)__end_rodata;
+#else
+ return false;
+#endif
+}
+
/* U-Boot-specific things begin here */
/* Start of U-Boot text region */
diff --git a/include/dm/of_access.h b/include/dm/of_access.h
index 44143a5a391..fe0de73d7e2 100644
--- a/include/dm/of_access.h
+++ b/include/dm/of_access.h
@@ -386,6 +386,23 @@ int of_read_u32_array(const struct device_node *np, const char *propname,
u32 *out_values, size_t sz);
/**
+ * of_read_u64_array() - Find and read an array of 64 bit integers
+ *
+ * Search for a property in a device node and read 64-bit value(s) from
+ * it.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ * Return:
+ * 0 on success, -EINVAL if the property does not exist, or -EOVERFLOW if
+ * longer than sz.
+ */
+int of_read_u64_array(const struct device_node *np, const char *propname,
+ u64 *out_values, size_t sz);
+
+/**
* of_property_match_string() - Find string in a list and return index
*
* This function searches a string list property and returns the index
@@ -616,6 +633,9 @@ int of_count_phandle_with_args(const struct device_node *np,
const char *list_name, const char *cells_name,
int cells_count);
+int of_property_count_elems_of_size(const struct device_node *np,
+ const char *propname, int elem_size);
+
/**
* of_alias_scan() - Scan all properties of the 'aliases' node
*
diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h
index 120393426db..c905e86b283 100644
--- a/include/dm/ofnode.h
+++ b/include/dm/ofnode.h
@@ -375,6 +375,13 @@ static inline oftree oftree_from_np(struct device_node *root)
return tree;
}
+/* Dummy put for Linux compat */
+static inline void ofnode_put(ofnode node)
+{
+ if (ofnode_is_np(node))
+ of_node_put(node.np);
+}
+
/**
* oftree_dispose() - Dispose of an oftree
*
@@ -589,6 +596,25 @@ int ofnode_read_u32_array(ofnode node, const char *propname,
u32 *out_values, size_t sz);
/**
+ * ofnode_read_u64_array() - Find and read an array of 64 bit integers
+ *
+ * @node: valid node reference to read property from
+ * @propname: name of the property to read
+ * @out_values: pointer to return value, modified only if return value is 0
+ * @sz: number of array elements to read
+ * Return: 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough
+ *
+ * Search for a property in a device node and read 64-bit value(s) from
+ * it.
+ *
+ * The out_values is modified only if a valid u64 value can be decoded.
+ */
+int ofnode_read_u64_array(ofnode node, const char *propname,
+ u64 *out_values, size_t sz);
+
+/**
* ofnode_read_bool() - read a boolean value from a property
*
* @node: valid node reference to read property from
@@ -652,6 +678,30 @@ static inline ofnode ofnode_next_subnode(ofnode node)
fdt_next_subnode(gd->fdt_blob, ofnode_to_offset(node)));
}
#else
+
+/**
+ * ofnode_count_elems_of_size() - count the number of elements of size @elem_size
+ * in the property @propname.
+ *
+ * @node: ofnode to check
+ * @propname: the name of the property to count
+ * @elem_size: the size of each element
+ *
+ * Returns: the number of elements or -EINVAL if the property size is not a
+ * multiple of elem_size.
+ */
+int ofnode_count_elems_of_size(ofnode node, const char *propname, int elem_size);
+
+static inline int ofnode_count_u32_elems(ofnode node, const char *propname)
+{
+ return ofnode_count_elems_of_size(node, propname, 4);
+}
+
+static inline int ofnode_count_u64_elems(ofnode node, const char *propname)
+{
+ return ofnode_count_elems_of_size(node, propname, 8);
+}
+
/**
* ofnode_is_enabled() - Checks whether a node is enabled.
* This looks for a 'status' property. If this exists, then returns true if
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 4e09f9d718c..46eaa0da63c 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -725,6 +725,22 @@ int fdtdec_get_int_array(const void *blob, int node, const char *prop_name,
u32 *array, int count);
/**
+ * Look up a property in a node and return its contents in a u64
+ * array of given length. The property must have at least enough data for
+ * the array (8*count bytes). It may have more, but this will be ignored.
+ *
+ * @param blob FDT blob
+ * @param node node to examine
+ * @param prop_name name of property to find
+ * @param array array to fill with data
+ * @param count number of array elements
+ * Return: 0 if ok, or -FDT_ERR_NOTFOUND if the property is not found,
+ * or -FDT_ERR_BADLAYOUT if not enough data
+ */
+int fdtdec_get_long_array(const void *blob, int node, const char *prop_name,
+ u64 *array, int count);
+
+/**
* Look up a property in a node and return its contents in an integer
* array of given length. The property must exist but may have less data that
* expected (4*count bytes). It may have more, but this will be ignored.
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 62381451617..d4ba4d0088a 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -67,6 +67,19 @@ static inline void vfree(const void *addr)
free((void *)addr);
}
+/**
+ * kstrdup_const - conditionally duplicate an existing const string
+ * @s: the string to duplicate
+ * @gfp: the GFP mask used in the kmalloc() call when allocating memory
+ *
+ * Note: Strings allocated by kstrdup_const should be freed by kfree_const and
+ * must not be passed to krealloc().
+ *
+ * Return: source string if it is in .rodata section otherwise
+ * fallback to kstrdup.
+ */
+#define kstrdup_const(s, gfp) strdup_const(s)
+
struct kmem_cache { int sz; };
struct kmem_cache *get_mem(int element_sz);
diff --git a/include/linux/device.h b/include/linux/device.h
new file mode 100644
index 00000000000..e76635cfde9
--- /dev/null
+++ b/include/linux/device.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * device.h - generic, centralized driver model
+ *
+ * U-Boot: compat header derived from Linux
+ *
+ * Copyright (c) 2001-2003 Patrick Mochel <[email protected]>
+ * Copyright (c) 2004-2009 Greg Kroah-Hartman <[email protected]>
+ * Copyright (c) 2008-2009 Novell Inc.
+ *
+ * See Documentation/driver-api/driver-model/ for more information.
+ */
+
+#ifndef _DEVICE_H_
+#define _DEVICE_H_
+
+#include <dm/device.h>
+
+/**
+ * dev_name - Return a device's name.
+ * @dev: Device with name to get.
+ * Return: The kobject name of the device, or its initial name if unavailable.
+ */
+static inline const char *dev_name(const struct udevice *dev)
+{
+ return dev->name;
+}
+
+#endif /* _DEVICE_H_ */
diff --git a/include/linux/err.h b/include/linux/err.h
index 7a0b212c813..2463c4611b2 100644
--- a/include/linux/err.h
+++ b/include/linux/err.h
@@ -53,6 +53,31 @@ static inline void * __must_check ERR_CAST(__force const void *ptr)
return (void *) ptr;
}
+/**
+ * PTR_ERR_OR_ZERO - Extract the error code from a pointer if it has one.
+ * @ptr: A potential error pointer.
+ *
+ * Convenience function that can be used inside a function that returns
+ * an error code to propagate errors received as error pointers.
+ * For example, ``return PTR_ERR_OR_ZERO(ptr);`` replaces:
+ *
+ * .. code-block:: c
+ *
+ * if (IS_ERR(ptr))
+ * return PTR_ERR(ptr);
+ * else
+ * return 0;
+ *
+ * Return: The error code within @ptr if it is an error pointer; 0 otherwise.
+ */
+static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr)
+{
+ if (IS_ERR(ptr))
+ return PTR_ERR(ptr);
+ else
+ return 0;
+}
+
#endif
#endif /* _LINUX_ERR_H */
diff --git a/include/linux/kref.h b/include/linux/kref.h
new file mode 100644
index 00000000000..30927047d54
--- /dev/null
+++ b/include/linux/kref.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * kref.h - library routines for handling generic reference counted objects
+ *
+ * Copyright (C) 2004 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2004 IBM Corp.
+ *
+ * based on kobject.h which was:
+ * Copyright (C) 2002-2003 Patrick Mochel <[email protected]>
+ * Copyright (C) 2002-2003 Open Source Development Labs
+ */
+
+#ifndef _KREF_H_
+#define _KREF_H_
+
+#include <linux/compat.h>
+
+struct kref {
+ long refcount;
+};
+
+#define KREF_INIT(n) { .refcount = REFCOUNT_INIT(n), }
+
+/**
+ * kref_init - initialize object.
+ * @kref: object in question.
+ */
+static inline void kref_init(struct kref *kref)
+{
+ kref->refcount = 1;
+}
+
+static inline unsigned int kref_read(const struct kref *kref)
+{
+ return kref->refcount;
+}
+
+/**
+ * kref_get - increment refcount for object.
+ * @kref: object.
+ */
+static inline void kref_get(struct kref *kref)
+{
+ kref->refcount++;
+}
+
+/**
+ * kref_put - Decrement refcount for object
+ * @kref: Object
+ * @release: Pointer to the function that will clean up the object when the
+ * last reference to the object is released.
+ *
+ * Decrement the refcount, and if 0, call @release. The caller may not
+ * pass NULL or kfree() as the release function.
+ *
+ * Return: 1 if this call removed the object, otherwise return 0. Beware,
+ * if this function returns 0, another caller may have removed the object
+ * by the time this function returns. The return value is only certain
+ * if you want to see if the object is definitely released.
+ */
+static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
+{
+ if (--kref->refcount == 0) {
+ release(kref);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * kref_put_mutex - Decrement refcount for object
+ * @kref: Object
+ * @release: Pointer to the function that will clean up the object when the
+ * last reference to the object is released.
+ * @mutex: Mutex which protects the release function.
+ *
+ * This variant of kref_lock() calls the @release function with the @mutex
+ * held. The @release function will release the mutex.
+ */
+static inline int kref_put_mutex(struct kref *kref,
+ void (*release)(struct kref *kref),
+ struct mutex *mutex)
+{
+ return kref_put(kref, release);
+}
+
+/**
+ * kref_put_lock - Decrement refcount for object
+ * @kref: Object
+ * @release: Pointer to the function that will clean up the object when the
+ * last reference to the object is released.
+ * @lock: Spinlock which protects the release function.
+ *
+ * This variant of kref_lock() calls the @release function with the @lock
+ * held. The @release function will release the lock.
+ */
+static inline int kref_put_lock(struct kref *kref,
+ void (*release)(struct kref *kref),
+ spinlock_t *lock)
+{
+ return kref_put(kref, release);
+}
+
+/**
+ * kref_get_unless_zero - Increment refcount for object unless it is zero.
+ * @kref: object.
+ *
+ * This function is intended to simplify locking around refcounting for
+ * objects that can be looked up from a lookup structure, and which are
+ * removed from that lookup structure in the object destructor.
+ * Operations on such objects require at least a read lock around
+ * lookup + kref_get, and a write lock around kref_put + remove from lookup
+ * structure. Furthermore, RCU implementations become extremely tricky.
+ * With a lookup followed by a kref_get_unless_zero *with return value check*
+ * locking in the kref_put path can be deferred to the actual removal from
+ * the lookup structure and RCU lookups become trivial.
+ *
+ * Return: non-zero if the increment succeeded. Otherwise return 0.
+ */
+static inline int kref_get_unless_zero(struct kref *kref)
+{
+ return kref->refcount ? kref->refcount++ : 0;
+}
+#endif /* _KREF_H_ */
diff --git a/include/linux/math64.h b/include/linux/math64.h
index eaa9fd5b968..70a7ee3ff1d 100644
--- a/include/linux/math64.h
+++ b/include/linux/math64.h
@@ -257,4 +257,23 @@ static inline u64 mul_u64_u32_div(u64 a, u32 mul, u32 divisor)
}
#endif /* mul_u64_u32_div */
+/**
+ * abs_diff - return absolute value of the difference between the arguments
+ * @a: the first argument
+ * @b: the second argument
+ *
+ * @a and @b have to be of the same type. With this restriction we compare
+ * signed to signed and unsigned to unsigned. The result is the subtraction
+ * the smaller of the two from the bigger, hence result is always a positive
+ * value.
+ *
+ * Return: an absolute value of the difference between the @a and @b.
+ */
+#define abs_diff(a, b) ({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ (void)(&__a == &__b); \
+ __a > __b ? (__a - __b) : (__b - __a); \
+})
+
#endif /* _LINUX_MATH64_H */
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
new file mode 120000
index 00000000000..8a3780b5ceb
--- /dev/null
+++ b/include/linux/regmap.h
@@ -0,0 +1 @@
+../regmap.h \ No newline at end of file
diff --git a/include/linux/string.h b/include/linux/string.h
index d943fcce690..a8a6cf4af50 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -104,6 +104,8 @@ size_t strcspn(const char *s, const char *reject);
#ifndef __HAVE_ARCH_STRDUP
extern char * strdup(const char *);
extern char * strndup(const char *, size_t);
+extern const char *strdup_const(const char *s);
+extern void kfree_const(const void *x);
#endif
#ifndef __HAVE_ARCH_STRSWAB
extern char * strswab(const char *);
diff --git a/include/power/regulator.h b/include/power/regulator.h
index 8a914dfc74f..4011fb1d254 100644
--- a/include/power/regulator.h
+++ b/include/power/regulator.h
@@ -359,6 +359,9 @@ int regulator_get_enable(struct udevice *dev);
*/
int regulator_set_enable(struct udevice *dev, bool enable);
+#define regulator_enable(dev) regulator_set_enable(dev, true)
+#define regulator_disable(dev) regulator_set_enable(dev, false)
+
/**
* regulator_set_enable_if_allowed: set regulator enable state if allowed by
* regulator
diff --git a/include/regmap.h b/include/regmap.h
index 8c6f7c1c9b1..eeae843bfb7 100644
--- a/include/regmap.h
+++ b/include/regmap.h
@@ -389,6 +389,15 @@ static inline int regmap_clear_bits(struct regmap *map, uint offset, uint bits)
return regmap_update_bits(map, offset, bits, 0);
}
+static inline int regmap_assign_bits(struct regmap *map, unsigned int reg,
+ unsigned int bits, bool value)
+{
+ if (value)
+ return regmap_set_bits(map, reg, bits);
+ else
+ return regmap_clear_bits(map, reg, bits);
+}
+
/**
* regmap_init_mem() - Set up a new register map that uses memory access
*
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index fb3375ea157..90d34ae105f 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -714,6 +714,24 @@ int fdtdec_get_int_array(const void *blob, int node, const char *prop_name,
return err;
}
+int fdtdec_get_long_array(const void *blob, int node, const char *prop_name,
+ u64 *array, int count)
+{
+ const u64 *cell;
+ int err = 0;
+
+ debug("%s: %s\n", __func__, prop_name);
+ cell = get_prop_check_min_len(blob, node, prop_name,
+ sizeof(u64) * count, &err);
+ if (!err) {
+ int i;
+
+ for (i = 0; i < count; i++)
+ array[i] = fdt64_to_cpu(cell[i]);
+ }
+ return err;
+}
+
int fdtdec_get_int_array_count(const void *blob, int node,
const char *prop_name, u32 *array, int count)
{
diff --git a/lib/string.c b/lib/string.c
index d56f88d4a84..302efe048b0 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -379,6 +379,37 @@ char * strndup(const char *s, size_t n)
return new;
}
+
+/**
+ * strdup_const - conditionally duplicate an existing const string
+ * @s: the string to duplicate
+ *
+ * Note: Strings allocated by kstrdup_const should be freed by kfree_const and
+ * must not be passed to krealloc().
+ *
+ * Return: source string if it is in .rodata section otherwise
+ * fallback to kstrdup.
+ */
+const char *strdup_const(const char *s)
+{
+ if (is_kernel_rodata((unsigned long)s))
+ return s;
+
+ return strdup(s);
+}
+
+/**
+ * kfree_const - conditionally free memory
+ * @x: pointer to the memory
+ *
+ * Function calls kfree only if @x is not in .rodata section.
+ */
+void kfree_const(const void *x)
+{
+ if (!is_kernel_rodata((unsigned long)x))
+ free((void *)x);
+}
+
#endif
#ifndef __HAVE_ARCH_STRSPN
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index c7340a047b2..49dc9c38c65 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -26,6 +26,11 @@
#include <linux/types.h>
#include <linux/string.h>
+/* For %pOF */
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+#include <dm/ofnode.h>
+#endif
+
/* we use this so that we can do without the ctype library */
#define is_digit(c) ((c) >= '0' && (c) <= '9')
@@ -438,6 +443,30 @@ static char *uuid_string(char *buf, char *end, u8 *addr, int field_width,
}
#endif
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !defined(API_BUILD)
+static char *ofnode_string(char *buf, char *end, ofnode *dp, int field_width,
+ int precision, int flags)
+{
+#define NP_PATH_MAX 64
+ char str[NP_PATH_MAX] = { 0 };
+ const char *err = "...";
+
+ /* If dp == NULL output the string '<NULL>' */
+ if (!dp || !ofnode_valid(*dp))
+ return string(buf, end, NULL, field_width, precision, flags);
+
+ /* Get the path and indicate if it got cut off */
+ if (ofnode_get_path(*dp, str, NP_PATH_MAX)) {
+ str[NP_PATH_MAX - 1] = '\0';
+ char *p = str + min((NP_PATH_MAX - 2) - strlen(err), strlen(str));
+ memcpy(p, err, strlen(err) + 1);
+ }
+
+ return string(buf, end, str, field_width, precision, flags);
+#undef NP_PATH_MAX
+}
+#endif
+
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
@@ -474,6 +503,14 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
return device_path_string(buf, end, ptr, field_width,
precision, flags);
#endif
+/* Device paths only exist in the EFI context. */
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !defined(API_BUILD)
+ case 'O':
+ if (fmt[1] == 'F')
+ return ofnode_string(buf, end, ptr, field_width,
+ precision, flags);
+ break;
+#endif
case 'a':
flags |= SPECIAL | ZEROPAD;