diff options
Diffstat (limited to 'lib/vsprintf.c')
| -rw-r--r-- | lib/vsprintf.c | 106 | 
1 files changed, 100 insertions, 6 deletions
| diff --git a/lib/vsprintf.c b/lib/vsprintf.c index ec337f64f52d..b235c96167d3 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -114,8 +114,9 @@ int skip_atoi(const char **s)  {  	int i = 0; -	while (isdigit(**s)) +	do {  		i = i*10 + *((*s)++) - '0'; +	} while (isdigit(**s));  	return i;  } @@ -793,6 +794,87 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,  }  static noinline_for_stack +char *bitmap_string(char *buf, char *end, unsigned long *bitmap, +		    struct printf_spec spec, const char *fmt) +{ +	const int CHUNKSZ = 32; +	int nr_bits = max_t(int, spec.field_width, 0); +	int i, chunksz; +	bool first = true; + +	/* reused to print numbers */ +	spec = (struct printf_spec){ .flags = SMALL | ZEROPAD, .base = 16 }; + +	chunksz = nr_bits & (CHUNKSZ - 1); +	if (chunksz == 0) +		chunksz = CHUNKSZ; + +	i = ALIGN(nr_bits, CHUNKSZ) - CHUNKSZ; +	for (; i >= 0; i -= CHUNKSZ) { +		u32 chunkmask, val; +		int word, bit; + +		chunkmask = ((1ULL << chunksz) - 1); +		word = i / BITS_PER_LONG; +		bit = i % BITS_PER_LONG; +		val = (bitmap[word] >> bit) & chunkmask; + +		if (!first) { +			if (buf < end) +				*buf = ','; +			buf++; +		} +		first = false; + +		spec.field_width = DIV_ROUND_UP(chunksz, 4); +		buf = number(buf, end, val, spec); + +		chunksz = CHUNKSZ; +	} +	return buf; +} + +static noinline_for_stack +char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap, +			 struct printf_spec spec, const char *fmt) +{ +	int nr_bits = max_t(int, spec.field_width, 0); +	/* current bit is 'cur', most recently seen range is [rbot, rtop] */ +	int cur, rbot, rtop; +	bool first = true; + +	/* reused to print numbers */ +	spec = (struct printf_spec){ .base = 10 }; + +	rbot = cur = find_first_bit(bitmap, nr_bits); +	while (cur < nr_bits) { +		rtop = cur; +		cur = find_next_bit(bitmap, nr_bits, cur + 1); +		if (cur < nr_bits && cur <= rtop + 1) +			continue; + +		if (!first) { +			if (buf < end) +				*buf = ','; +			buf++; +		} +		first = false; + +		buf = number(buf, end, rbot, spec); +		if (rbot < rtop) { +			if (buf < end) +				*buf = '-'; +			buf++; + +			buf = number(buf, end, rtop, spec); +		} + +		rbot = cur; +	} +	return buf; +} + +static noinline_for_stack  char *mac_address_string(char *buf, char *end, u8 *addr,  			 struct printf_spec spec, const char *fmt)  { @@ -1257,6 +1339,10 @@ int kptr_restrict __read_mostly;   * - 'B' For backtraced symbolic direct pointers with offset   * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref]   * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201] + * - 'b[l]' For a bitmap, the number of bits is determined by the field + *       width which must be explicitly specified either as part of the + *       format string '%32b[l]' or through '%*b[l]', [l] selects + *       range-list format instead of hex format   * - 'M' For a 6-byte MAC address, it prints the address in the   *       usual colon-separated hex notation   * - 'm' For a 6-byte MAC address, it prints the hex address without colons @@ -1353,6 +1439,13 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,  		return resource_string(buf, end, ptr, spec, fmt);  	case 'h':  		return hex_string(buf, end, ptr, spec, fmt); +	case 'b': +		switch (fmt[1]) { +		case 'l': +			return bitmap_list_string(buf, end, ptr, spec, fmt); +		default: +			return bitmap_string(buf, end, ptr, spec, fmt); +		}  	case 'M':			/* Colon separated: 00:01:02:03:04:05 */  	case 'm':			/* Contiguous: 000102030405 */  					/* [mM]F (FDDI) */ @@ -1604,8 +1697,7 @@ qualifier:  	case 'p':  		spec->type = FORMAT_TYPE_PTR; -		return fmt - start; -		/* skip alnum */ +		return ++fmt - start;  	case '%':  		spec->type = FORMAT_TYPE_PERCENT_CHAR; @@ -1689,6 +1781,8 @@ qualifier:   * %pB output the name of a backtrace symbol with its offset   * %pR output the address range in a struct resource with decoded flags   * %pr output the address range in a struct resource with raw flags + * %pb output the bitmap with field width as the number of bits + * %pbl output the bitmap as range list with field width as the number of bits   * %pM output a 6-byte MAC address with colons   * %pMR output a 6-byte MAC address with colons in reversed order   * %pMF output a 6-byte MAC address with dashes @@ -1728,7 +1822,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)  	/* Reject out-of-range values early.  Large positive sizes are  	   used for unknown buffer sizes. */ -	if (WARN_ON_ONCE((int) size < 0)) +	if (WARN_ON_ONCE(size > INT_MAX))  		return 0;  	str = buf; @@ -1794,7 +1888,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)  			break;  		case FORMAT_TYPE_PTR: -			str = pointer(fmt+1, str, end, va_arg(args, void *), +			str = pointer(fmt, str, end, va_arg(args, void *),  				      spec);  			while (isalnum(*fmt))  				fmt++; @@ -2232,7 +2326,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)  		}  		case FORMAT_TYPE_PTR: -			str = pointer(fmt+1, str, end, get_arg(void *), spec); +			str = pointer(fmt, str, end, get_arg(void *), spec);  			while (isalnum(*fmt))  				fmt++;  			break; |