diff options
| author | Tom Rini <[email protected]> | 2026-04-21 11:21:59 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-04-21 11:21:59 -0600 |
| commit | 052988aa29bfd506d7ce207fbb3f5374a5dbecbb (patch) | |
| tree | 7eae9dc76b1383368733804f5e2801faaaa481a8 | |
| parent | 5d401bfbdf1da9eb34575b0b15e18757f2b38ca0 (diff) | |
| parent | f1f4a1d1d8355b0fc6bfc82d9ca6741a8e92623b (diff) | |
Merge patch series "Linux compat improvements and CCF prep"
Casey Connolly <[email protected]> says:
This series implements various improvements to Linux header
compatibility, largely in preparation for a full port of Linux CCF but
many of these changes would also be helpful when porting other drivers.
Beside the basic header/compat stuff there are a few larger patches:
Patch 1 adds the "%pOF" format specifier to vsprintf, this behaves the
same as it does in Linux printing the name of the ofnode, but notably it
expects an ofnode pointer rather than a device_node.
Patch 2 adds an option to skip doing a full DM scan pre-relocation.
Some platforms like Qualcomm don't actually need devices to be probed
prior to relocation, it is also quite slow to scan the entire FDT before
caches are up. This option gets us to main loop 30-50% faster.
Unfortunately it isn't possible to totally skip DM since U-Boot will
panic if it can't find a serial port, but the serial uclass code will
bind the serial port itself by reading /chosen/stdout-path, however any
dependencies like clocks won't be found so this should only be enabled
if the serial driver gracefully handles missing clocks.
Patch 3 adds [k]strdup_const(), this works the same as the Linux version
saving a small amount of memory by avoiding duplicating strings stored
in .rodata, this is particularly useful for CCF.
Patch 4 adds 64-bit versions of some 32-bit ofnode utilities functions,
making it possible to parse 64-bit arrays.
Patch 6 provides a simple implementation of kref, this will be used
by CCF.
Patch 9 adds devm_krealloc() support to devres, it relies on storing
allocation sizes in the devres struct which will add a small overhead.
Link: https://lore.kernel.org/r/[email protected]
| -rw-r--r-- | Kconfig | 12 | ||||
| -rw-r--r-- | common/board_f.c | 11 | ||||
| -rw-r--r-- | drivers/core/of_access.c | 52 | ||||
| -rw-r--r-- | drivers/core/ofnode.c | 48 | ||||
| -rw-r--r-- | include/asm-generic/sections.h | 19 | ||||
| -rw-r--r-- | include/dm/of_access.h | 20 | ||||
| -rw-r--r-- | include/dm/ofnode.h | 50 | ||||
| -rw-r--r-- | include/fdtdec.h | 16 | ||||
| -rw-r--r-- | include/linux/compat.h | 13 | ||||
| -rw-r--r-- | include/linux/device.h | 29 | ||||
| -rw-r--r-- | include/linux/err.h | 25 | ||||
| -rw-r--r-- | include/linux/kref.h | 124 | ||||
| -rw-r--r-- | include/linux/math64.h | 19 | ||||
| l--------- | include/linux/regmap.h | 1 | ||||
| -rw-r--r-- | include/linux/string.h | 2 | ||||
| -rw-r--r-- | include/power/regulator.h | 3 | ||||
| -rw-r--r-- | include/regmap.h | 9 | ||||
| -rw-r--r-- | lib/fdtdec.c | 18 | ||||
| -rw-r--r-- | lib/string.c | 31 | ||||
| -rw-r--r-- | lib/vsprintf.c | 37 |
20 files changed, 538 insertions, 1 deletions
@@ -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; |
