diff options
Diffstat (limited to 'lib/vsprintf.c')
| -rw-r--r-- | lib/vsprintf.c | 117 | 
1 files changed, 108 insertions, 9 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 41ddc353ebb8..f0c35d9b65bf 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1734,6 +1734,42 @@ char *netdev_bits(char *buf, char *end, const void *addr,  }  static noinline_for_stack +char *fourcc_string(char *buf, char *end, const u32 *fourcc, +		    struct printf_spec spec, const char *fmt) +{ +	char output[sizeof("0123 little-endian (0x01234567)")]; +	char *p = output; +	unsigned int i; +	u32 val; + +	if (fmt[1] != 'c' || fmt[2] != 'c') +		return error_string(buf, end, "(%p4?)", spec); + +	if (check_pointer(&buf, end, fourcc, spec)) +		return buf; + +	val = *fourcc & ~BIT(31); + +	for (i = 0; i < sizeof(*fourcc); i++) { +		unsigned char c = val >> (i * 8); + +		/* Print non-control ASCII characters as-is, dot otherwise */ +		*p++ = isascii(c) && isprint(c) ? c : '.'; +	} + +	strcpy(p, *fourcc & BIT(31) ? " big-endian" : " little-endian"); +	p += strlen(p); + +	*p++ = ' '; +	*p++ = '('; +	p = special_hex_number(p, output + sizeof(output) - 2, *fourcc, sizeof(u32)); +	*p++ = ')'; +	*p = '\0'; + +	return string(buf, end, output, spec); +} + +static noinline_for_stack  char *address_val(char *buf, char *end, const void *addr,  		  struct printf_spec spec, const char *fmt)  { @@ -1916,6 +1952,66 @@ char *format_flags(char *buf, char *end, unsigned long flags,  	return buf;  } +struct page_flags_fields { +	int width; +	int shift; +	int mask; +	const struct printf_spec *spec; +	const char *name; +}; + +static const struct page_flags_fields pff[] = { +	{SECTIONS_WIDTH, SECTIONS_PGSHIFT, SECTIONS_MASK, +	 &default_dec_spec, "section"}, +	{NODES_WIDTH, NODES_PGSHIFT, NODES_MASK, +	 &default_dec_spec, "node"}, +	{ZONES_WIDTH, ZONES_PGSHIFT, ZONES_MASK, +	 &default_dec_spec, "zone"}, +	{LAST_CPUPID_WIDTH, LAST_CPUPID_PGSHIFT, LAST_CPUPID_MASK, +	 &default_flag_spec, "lastcpupid"}, +	{KASAN_TAG_WIDTH, KASAN_TAG_PGSHIFT, KASAN_TAG_MASK, +	 &default_flag_spec, "kasantag"}, +}; + +static +char *format_page_flags(char *buf, char *end, unsigned long flags) +{ +	unsigned long main_flags = flags & (BIT(NR_PAGEFLAGS) - 1); +	bool append = false; +	int i; + +	/* Page flags from the main area. */ +	if (main_flags) { +		buf = format_flags(buf, end, main_flags, pageflag_names); +		append = true; +	} + +	/* Page flags from the fields area */ +	for (i = 0; i < ARRAY_SIZE(pff); i++) { +		/* Skip undefined fields. */ +		if (!pff[i].width) +			continue; + +		/* Format: Flag Name + '=' (equals sign) + Number + '|' (separator) */ +		if (append) { +			if (buf < end) +				*buf = '|'; +			buf++; +		} + +		buf = string(buf, end, pff[i].name, default_str_spec); +		if (buf < end) +			*buf = '='; +		buf++; +		buf = number(buf, end, (flags >> pff[i].shift) & pff[i].mask, +			     *pff[i].spec); + +		append = true; +	} + +	return buf; +} +  static noinline_for_stack  char *flags_string(char *buf, char *end, void *flags_ptr,  		   struct printf_spec spec, const char *fmt) @@ -1928,11 +2024,7 @@ char *flags_string(char *buf, char *end, void *flags_ptr,  	switch (fmt[1]) {  	case 'p': -		flags = *(unsigned long *)flags_ptr; -		/* Remove zone id */ -		flags &= (1UL << NR_PAGEFLAGS) - 1; -		names = pageflag_names; -		break; +		return format_page_flags(buf, end, *(unsigned long *)flags_ptr);  	case 'v':  		flags = *(unsigned long *)flags_ptr;  		names = vmaflag_names; @@ -2096,6 +2188,9 @@ EXPORT_SYMBOL_GPL(no_hash_pointers);  static int __init no_hash_pointers_enable(char *str)  { +	if (no_hash_pointers) +		return 0; +  	no_hash_pointers = true;  	pr_warn("**********************************************************\n"); @@ -2186,8 +2281,11 @@ early_param("no_hash_pointers", no_hash_pointers_enable);   *       Implements a "recursive vsnprintf".   *       Do not use this feature without some mechanism to verify the   *       correctness of the format string and va_list arguments. - * - 'K' For a kernel pointer that should be hidden from unprivileged users + * - 'K' For a kernel pointer that should be hidden from unprivileged users. + *       Use only for procfs, sysfs and similar files, not printk(); please + *       read the documentation (path below) first.   * - 'NF' For a netdev_features_t + * - '4cc' V4L2 or DRM FourCC code, with endianness and raw numerical value.   * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with   *            a certain separator (' ' by default):   *              C colon @@ -2225,7 +2323,8 @@ early_param("no_hash_pointers", no_hash_pointers_enable);   *		Without an option prints the full name of the node   *		f full name   *		P node name, including a possible unit address - * - 'x' For printing the address. Equivalent to "%lx". + * - 'x' For printing the address unmodified. Equivalent to "%lx". + *       Please read the documentation (path below) before using!   * - '[ku]s' For a BPF/tracing related format specifier, e.g. used out of   *           bpf_trace_printk() where [ku] prefix specifies either kernel (k)   *           or user (u) memory to probe, and: @@ -2285,6 +2384,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,  		return restricted_pointer(buf, end, ptr, spec);  	case 'N':  		return netdev_bits(buf, end, ptr, spec, fmt); +	case '4': +		return fourcc_string(buf, end, ptr, spec, fmt);  	case 'a':  		return address_val(buf, end, ptr, spec, fmt);  	case 'd': @@ -3135,8 +3236,6 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)  			switch (*fmt) {  			case 'S':  			case 's': -			case 'F': -			case 'f':  			case 'x':  			case 'K':  			case 'e':  |