162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/lib/vsprintf.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Wirzenius wrote this portably, Torvalds fucked it up :-) 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com> 1562306a36Sopenharmony_ci * - changed to provide snprintf and vsnprintf functions 1662306a36Sopenharmony_ci * So Feb 1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de> 1762306a36Sopenharmony_ci * - scnprintf and vscnprintf 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/stdarg.h> 2162306a36Sopenharmony_ci#include <linux/build_bug.h> 2262306a36Sopenharmony_ci#include <linux/clk.h> 2362306a36Sopenharmony_ci#include <linux/clk-provider.h> 2462306a36Sopenharmony_ci#include <linux/errname.h> 2562306a36Sopenharmony_ci#include <linux/module.h> /* for KSYM_SYMBOL_LEN */ 2662306a36Sopenharmony_ci#include <linux/types.h> 2762306a36Sopenharmony_ci#include <linux/string.h> 2862306a36Sopenharmony_ci#include <linux/ctype.h> 2962306a36Sopenharmony_ci#include <linux/kernel.h> 3062306a36Sopenharmony_ci#include <linux/kallsyms.h> 3162306a36Sopenharmony_ci#include <linux/math64.h> 3262306a36Sopenharmony_ci#include <linux/uaccess.h> 3362306a36Sopenharmony_ci#include <linux/ioport.h> 3462306a36Sopenharmony_ci#include <linux/dcache.h> 3562306a36Sopenharmony_ci#include <linux/cred.h> 3662306a36Sopenharmony_ci#include <linux/rtc.h> 3762306a36Sopenharmony_ci#include <linux/sprintf.h> 3862306a36Sopenharmony_ci#include <linux/time.h> 3962306a36Sopenharmony_ci#include <linux/uuid.h> 4062306a36Sopenharmony_ci#include <linux/of.h> 4162306a36Sopenharmony_ci#include <net/addrconf.h> 4262306a36Sopenharmony_ci#include <linux/siphash.h> 4362306a36Sopenharmony_ci#include <linux/compiler.h> 4462306a36Sopenharmony_ci#include <linux/property.h> 4562306a36Sopenharmony_ci#include <linux/notifier.h> 4662306a36Sopenharmony_ci#ifdef CONFIG_BLOCK 4762306a36Sopenharmony_ci#include <linux/blkdev.h> 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include "../mm/internal.h" /* For the trace_print_flags arrays */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#include <asm/page.h> /* for PAGE_SIZE */ 5362306a36Sopenharmony_ci#include <asm/byteorder.h> /* cpu_to_le16 */ 5462306a36Sopenharmony_ci#include <asm/unaligned.h> 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#include <linux/string_helpers.h> 5762306a36Sopenharmony_ci#include "kstrtox.h" 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Disable pointer hashing if requested */ 6062306a36Sopenharmony_cibool no_hash_pointers __ro_after_init; 6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(no_hash_pointers); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic noinline unsigned long long simple_strntoull(const char *startp, size_t max_chars, char **endp, unsigned int base) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci const char *cp; 6662306a36Sopenharmony_ci unsigned long long result = 0ULL; 6762306a36Sopenharmony_ci size_t prefix_chars; 6862306a36Sopenharmony_ci unsigned int rv; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci cp = _parse_integer_fixup_radix(startp, &base); 7162306a36Sopenharmony_ci prefix_chars = cp - startp; 7262306a36Sopenharmony_ci if (prefix_chars < max_chars) { 7362306a36Sopenharmony_ci rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars); 7462306a36Sopenharmony_ci /* FIXME */ 7562306a36Sopenharmony_ci cp += (rv & ~KSTRTOX_OVERFLOW); 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci /* Field too short for prefix + digit, skip over without converting */ 7862306a36Sopenharmony_ci cp = startp + max_chars; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (endp) 8262306a36Sopenharmony_ci *endp = (char *)cp; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return result; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/** 8862306a36Sopenharmony_ci * simple_strtoull - convert a string to an unsigned long long 8962306a36Sopenharmony_ci * @cp: The start of the string 9062306a36Sopenharmony_ci * @endp: A pointer to the end of the parsed string will be placed here 9162306a36Sopenharmony_ci * @base: The number base to use 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * This function has caveats. Please use kstrtoull instead. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cinoinline 9662306a36Sopenharmony_ciunsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci return simple_strntoull(cp, INT_MAX, endp, base); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ciEXPORT_SYMBOL(simple_strtoull); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/** 10362306a36Sopenharmony_ci * simple_strtoul - convert a string to an unsigned long 10462306a36Sopenharmony_ci * @cp: The start of the string 10562306a36Sopenharmony_ci * @endp: A pointer to the end of the parsed string will be placed here 10662306a36Sopenharmony_ci * @base: The number base to use 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * This function has caveats. Please use kstrtoul instead. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ciunsigned long simple_strtoul(const char *cp, char **endp, unsigned int base) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci return simple_strtoull(cp, endp, base); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ciEXPORT_SYMBOL(simple_strtoul); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/** 11762306a36Sopenharmony_ci * simple_strtol - convert a string to a signed long 11862306a36Sopenharmony_ci * @cp: The start of the string 11962306a36Sopenharmony_ci * @endp: A pointer to the end of the parsed string will be placed here 12062306a36Sopenharmony_ci * @base: The number base to use 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * This function has caveats. Please use kstrtol instead. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cilong simple_strtol(const char *cp, char **endp, unsigned int base) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci if (*cp == '-') 12762306a36Sopenharmony_ci return -simple_strtoul(cp + 1, endp, base); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return simple_strtoul(cp, endp, base); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ciEXPORT_SYMBOL(simple_strtol); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic long long simple_strntoll(const char *cp, size_t max_chars, char **endp, 13462306a36Sopenharmony_ci unsigned int base) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * simple_strntoull() safely handles receiving max_chars==0 in the 13862306a36Sopenharmony_ci * case cp[0] == '-' && max_chars == 1. 13962306a36Sopenharmony_ci * If max_chars == 0 we can drop through and pass it to simple_strntoull() 14062306a36Sopenharmony_ci * and the content of *cp is irrelevant. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci if (*cp == '-' && max_chars > 0) 14362306a36Sopenharmony_ci return -simple_strntoull(cp + 1, max_chars - 1, endp, base); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return simple_strntoull(cp, max_chars, endp, base); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/** 14962306a36Sopenharmony_ci * simple_strtoll - convert a string to a signed long long 15062306a36Sopenharmony_ci * @cp: The start of the string 15162306a36Sopenharmony_ci * @endp: A pointer to the end of the parsed string will be placed here 15262306a36Sopenharmony_ci * @base: The number base to use 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * This function has caveats. Please use kstrtoll instead. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_cilong long simple_strtoll(const char *cp, char **endp, unsigned int base) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return simple_strntoll(cp, INT_MAX, endp, base); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ciEXPORT_SYMBOL(simple_strtoll); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic noinline_for_stack 16362306a36Sopenharmony_ciint skip_atoi(const char **s) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci int i = 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci do { 16862306a36Sopenharmony_ci i = i*10 + *((*s)++) - '0'; 16962306a36Sopenharmony_ci } while (isdigit(**s)); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return i; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci * Decimal conversion is by far the most typical, and is used for 17662306a36Sopenharmony_ci * /proc and /sys data. This directly impacts e.g. top performance 17762306a36Sopenharmony_ci * with many processes running. We optimize it for speed by emitting 17862306a36Sopenharmony_ci * two characters at a time, using a 200 byte lookup table. This 17962306a36Sopenharmony_ci * roughly halves the number of multiplications compared to computing 18062306a36Sopenharmony_ci * the digits one at a time. Implementation strongly inspired by the 18162306a36Sopenharmony_ci * previous version, which in turn used ideas described at 18262306a36Sopenharmony_ci * <http://www.cs.uiowa.edu/~jones/bcd/divide.html> (with permission 18362306a36Sopenharmony_ci * from the author, Douglas W. Jones). 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * It turns out there is precisely one 26 bit fixed-point 18662306a36Sopenharmony_ci * approximation a of 64/100 for which x/100 == (x * (u64)a) >> 32 18762306a36Sopenharmony_ci * holds for all x in [0, 10^8-1], namely a = 0x28f5c29. The actual 18862306a36Sopenharmony_ci * range happens to be somewhat larger (x <= 1073741898), but that's 18962306a36Sopenharmony_ci * irrelevant for our purpose. 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * For dividing a number in the range [10^4, 10^6-1] by 100, we still 19262306a36Sopenharmony_ci * need a 32x32->64 bit multiply, so we simply use the same constant. 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * For dividing a number in the range [100, 10^4-1] by 100, there are 19562306a36Sopenharmony_ci * several options. The simplest is (x * 0x147b) >> 19, which is valid 19662306a36Sopenharmony_ci * for all x <= 43698. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const u16 decpair[100] = { 20062306a36Sopenharmony_ci#define _(x) (__force u16) cpu_to_le16(((x % 10) | ((x / 10) << 8)) + 0x3030) 20162306a36Sopenharmony_ci _( 0), _( 1), _( 2), _( 3), _( 4), _( 5), _( 6), _( 7), _( 8), _( 9), 20262306a36Sopenharmony_ci _(10), _(11), _(12), _(13), _(14), _(15), _(16), _(17), _(18), _(19), 20362306a36Sopenharmony_ci _(20), _(21), _(22), _(23), _(24), _(25), _(26), _(27), _(28), _(29), 20462306a36Sopenharmony_ci _(30), _(31), _(32), _(33), _(34), _(35), _(36), _(37), _(38), _(39), 20562306a36Sopenharmony_ci _(40), _(41), _(42), _(43), _(44), _(45), _(46), _(47), _(48), _(49), 20662306a36Sopenharmony_ci _(50), _(51), _(52), _(53), _(54), _(55), _(56), _(57), _(58), _(59), 20762306a36Sopenharmony_ci _(60), _(61), _(62), _(63), _(64), _(65), _(66), _(67), _(68), _(69), 20862306a36Sopenharmony_ci _(70), _(71), _(72), _(73), _(74), _(75), _(76), _(77), _(78), _(79), 20962306a36Sopenharmony_ci _(80), _(81), _(82), _(83), _(84), _(85), _(86), _(87), _(88), _(89), 21062306a36Sopenharmony_ci _(90), _(91), _(92), _(93), _(94), _(95), _(96), _(97), _(98), _(99), 21162306a36Sopenharmony_ci#undef _ 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * This will print a single '0' even if r == 0, since we would 21662306a36Sopenharmony_ci * immediately jump to out_r where two 0s would be written but only 21762306a36Sopenharmony_ci * one of them accounted for in buf. This is needed by ip4_string 21862306a36Sopenharmony_ci * below. All other callers pass a non-zero value of r. 21962306a36Sopenharmony_ci*/ 22062306a36Sopenharmony_cistatic noinline_for_stack 22162306a36Sopenharmony_cichar *put_dec_trunc8(char *buf, unsigned r) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci unsigned q; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* 1 <= r < 10^8 */ 22662306a36Sopenharmony_ci if (r < 100) 22762306a36Sopenharmony_ci goto out_r; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* 100 <= r < 10^8 */ 23062306a36Sopenharmony_ci q = (r * (u64)0x28f5c29) >> 32; 23162306a36Sopenharmony_ci *((u16 *)buf) = decpair[r - 100*q]; 23262306a36Sopenharmony_ci buf += 2; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* 1 <= q < 10^6 */ 23562306a36Sopenharmony_ci if (q < 100) 23662306a36Sopenharmony_ci goto out_q; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* 100 <= q < 10^6 */ 23962306a36Sopenharmony_ci r = (q * (u64)0x28f5c29) >> 32; 24062306a36Sopenharmony_ci *((u16 *)buf) = decpair[q - 100*r]; 24162306a36Sopenharmony_ci buf += 2; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 1 <= r < 10^4 */ 24462306a36Sopenharmony_ci if (r < 100) 24562306a36Sopenharmony_ci goto out_r; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 100 <= r < 10^4 */ 24862306a36Sopenharmony_ci q = (r * 0x147b) >> 19; 24962306a36Sopenharmony_ci *((u16 *)buf) = decpair[r - 100*q]; 25062306a36Sopenharmony_ci buf += 2; 25162306a36Sopenharmony_ciout_q: 25262306a36Sopenharmony_ci /* 1 <= q < 100 */ 25362306a36Sopenharmony_ci r = q; 25462306a36Sopenharmony_ciout_r: 25562306a36Sopenharmony_ci /* 1 <= r < 100 */ 25662306a36Sopenharmony_ci *((u16 *)buf) = decpair[r]; 25762306a36Sopenharmony_ci buf += r < 10 ? 1 : 2; 25862306a36Sopenharmony_ci return buf; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci#if BITS_PER_LONG == 64 && BITS_PER_LONG_LONG == 64 26262306a36Sopenharmony_cistatic noinline_for_stack 26362306a36Sopenharmony_cichar *put_dec_full8(char *buf, unsigned r) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci unsigned q; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 0 <= r < 10^8 */ 26862306a36Sopenharmony_ci q = (r * (u64)0x28f5c29) >> 32; 26962306a36Sopenharmony_ci *((u16 *)buf) = decpair[r - 100*q]; 27062306a36Sopenharmony_ci buf += 2; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 0 <= q < 10^6 */ 27362306a36Sopenharmony_ci r = (q * (u64)0x28f5c29) >> 32; 27462306a36Sopenharmony_ci *((u16 *)buf) = decpair[q - 100*r]; 27562306a36Sopenharmony_ci buf += 2; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* 0 <= r < 10^4 */ 27862306a36Sopenharmony_ci q = (r * 0x147b) >> 19; 27962306a36Sopenharmony_ci *((u16 *)buf) = decpair[r - 100*q]; 28062306a36Sopenharmony_ci buf += 2; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 0 <= q < 100 */ 28362306a36Sopenharmony_ci *((u16 *)buf) = decpair[q]; 28462306a36Sopenharmony_ci buf += 2; 28562306a36Sopenharmony_ci return buf; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic noinline_for_stack 28962306a36Sopenharmony_cichar *put_dec(char *buf, unsigned long long n) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci if (n >= 100*1000*1000) 29262306a36Sopenharmony_ci buf = put_dec_full8(buf, do_div(n, 100*1000*1000)); 29362306a36Sopenharmony_ci /* 1 <= n <= 1.6e11 */ 29462306a36Sopenharmony_ci if (n >= 100*1000*1000) 29562306a36Sopenharmony_ci buf = put_dec_full8(buf, do_div(n, 100*1000*1000)); 29662306a36Sopenharmony_ci /* 1 <= n < 1e8 */ 29762306a36Sopenharmony_ci return put_dec_trunc8(buf, n); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci#elif BITS_PER_LONG == 32 && BITS_PER_LONG_LONG == 64 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic void 30362306a36Sopenharmony_ciput_dec_full4(char *buf, unsigned r) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci unsigned q; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* 0 <= r < 10^4 */ 30862306a36Sopenharmony_ci q = (r * 0x147b) >> 19; 30962306a36Sopenharmony_ci *((u16 *)buf) = decpair[r - 100*q]; 31062306a36Sopenharmony_ci buf += 2; 31162306a36Sopenharmony_ci /* 0 <= q < 100 */ 31262306a36Sopenharmony_ci *((u16 *)buf) = decpair[q]; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* 31662306a36Sopenharmony_ci * Call put_dec_full4 on x % 10000, return x / 10000. 31762306a36Sopenharmony_ci * The approximation x/10000 == (x * 0x346DC5D7) >> 43 31862306a36Sopenharmony_ci * holds for all x < 1,128,869,999. The largest value this 31962306a36Sopenharmony_ci * helper will ever be asked to convert is 1,125,520,955. 32062306a36Sopenharmony_ci * (second call in the put_dec code, assuming n is all-ones). 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic noinline_for_stack 32362306a36Sopenharmony_ciunsigned put_dec_helper4(char *buf, unsigned x) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci uint32_t q = (x * (uint64_t)0x346DC5D7) >> 43; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci put_dec_full4(buf, x - q * 10000); 32862306a36Sopenharmony_ci return q; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/* Based on code by Douglas W. Jones found at 33262306a36Sopenharmony_ci * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour> 33362306a36Sopenharmony_ci * (with permission from the author). 33462306a36Sopenharmony_ci * Performs no 64-bit division and hence should be fast on 32-bit machines. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_cistatic 33762306a36Sopenharmony_cichar *put_dec(char *buf, unsigned long long n) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci uint32_t d3, d2, d1, q, h; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (n < 100*1000*1000) 34262306a36Sopenharmony_ci return put_dec_trunc8(buf, n); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci d1 = ((uint32_t)n >> 16); /* implicit "& 0xffff" */ 34562306a36Sopenharmony_ci h = (n >> 32); 34662306a36Sopenharmony_ci d2 = (h ) & 0xffff; 34762306a36Sopenharmony_ci d3 = (h >> 16); /* implicit "& 0xffff" */ 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0 35062306a36Sopenharmony_ci = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */ 35162306a36Sopenharmony_ci q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((uint32_t)n & 0xffff); 35262306a36Sopenharmony_ci q = put_dec_helper4(buf, q); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci q += 7671 * d3 + 9496 * d2 + 6 * d1; 35562306a36Sopenharmony_ci q = put_dec_helper4(buf+4, q); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci q += 4749 * d3 + 42 * d2; 35862306a36Sopenharmony_ci q = put_dec_helper4(buf+8, q); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci q += 281 * d3; 36162306a36Sopenharmony_ci buf += 12; 36262306a36Sopenharmony_ci if (q) 36362306a36Sopenharmony_ci buf = put_dec_trunc8(buf, q); 36462306a36Sopenharmony_ci else while (buf[-1] == '0') 36562306a36Sopenharmony_ci --buf; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return buf; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci#endif 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/* 37362306a36Sopenharmony_ci * Convert passed number to decimal string. 37462306a36Sopenharmony_ci * Returns the length of string. On buffer overflow, returns 0. 37562306a36Sopenharmony_ci * 37662306a36Sopenharmony_ci * If speed is not important, use snprintf(). It's easy to read the code. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ciint num_to_str(char *buf, int size, unsigned long long num, unsigned int width) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci /* put_dec requires 2-byte alignment of the buffer. */ 38162306a36Sopenharmony_ci char tmp[sizeof(num) * 3] __aligned(2); 38262306a36Sopenharmony_ci int idx, len; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* put_dec() may work incorrectly for num = 0 (generate "", not "0") */ 38562306a36Sopenharmony_ci if (num <= 9) { 38662306a36Sopenharmony_ci tmp[0] = '0' + num; 38762306a36Sopenharmony_ci len = 1; 38862306a36Sopenharmony_ci } else { 38962306a36Sopenharmony_ci len = put_dec(tmp, num) - tmp; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (len > size || width > size) 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (width > len) { 39662306a36Sopenharmony_ci width = width - len; 39762306a36Sopenharmony_ci for (idx = 0; idx < width; idx++) 39862306a36Sopenharmony_ci buf[idx] = ' '; 39962306a36Sopenharmony_ci } else { 40062306a36Sopenharmony_ci width = 0; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci for (idx = 0; idx < len; ++idx) 40462306a36Sopenharmony_ci buf[idx + width] = tmp[len - idx - 1]; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return len + width; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci#define SIGN 1 /* unsigned/signed, must be 1 */ 41062306a36Sopenharmony_ci#define LEFT 2 /* left justified */ 41162306a36Sopenharmony_ci#define PLUS 4 /* show plus */ 41262306a36Sopenharmony_ci#define SPACE 8 /* space if plus */ 41362306a36Sopenharmony_ci#define ZEROPAD 16 /* pad with zero, must be 16 == '0' - ' ' */ 41462306a36Sopenharmony_ci#define SMALL 32 /* use lowercase in hex (must be 32 == 0x20) */ 41562306a36Sopenharmony_ci#define SPECIAL 64 /* prefix hex with "0x", octal with "0" */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic_assert(SIGN == 1); 41862306a36Sopenharmony_cistatic_assert(ZEROPAD == ('0' - ' ')); 41962306a36Sopenharmony_cistatic_assert(SMALL == ('a' ^ 'A')); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cienum format_type { 42262306a36Sopenharmony_ci FORMAT_TYPE_NONE, /* Just a string part */ 42362306a36Sopenharmony_ci FORMAT_TYPE_WIDTH, 42462306a36Sopenharmony_ci FORMAT_TYPE_PRECISION, 42562306a36Sopenharmony_ci FORMAT_TYPE_CHAR, 42662306a36Sopenharmony_ci FORMAT_TYPE_STR, 42762306a36Sopenharmony_ci FORMAT_TYPE_PTR, 42862306a36Sopenharmony_ci FORMAT_TYPE_PERCENT_CHAR, 42962306a36Sopenharmony_ci FORMAT_TYPE_INVALID, 43062306a36Sopenharmony_ci FORMAT_TYPE_LONG_LONG, 43162306a36Sopenharmony_ci FORMAT_TYPE_ULONG, 43262306a36Sopenharmony_ci FORMAT_TYPE_LONG, 43362306a36Sopenharmony_ci FORMAT_TYPE_UBYTE, 43462306a36Sopenharmony_ci FORMAT_TYPE_BYTE, 43562306a36Sopenharmony_ci FORMAT_TYPE_USHORT, 43662306a36Sopenharmony_ci FORMAT_TYPE_SHORT, 43762306a36Sopenharmony_ci FORMAT_TYPE_UINT, 43862306a36Sopenharmony_ci FORMAT_TYPE_INT, 43962306a36Sopenharmony_ci FORMAT_TYPE_SIZE_T, 44062306a36Sopenharmony_ci FORMAT_TYPE_PTRDIFF 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistruct printf_spec { 44462306a36Sopenharmony_ci unsigned int type:8; /* format_type enum */ 44562306a36Sopenharmony_ci signed int field_width:24; /* width of output field */ 44662306a36Sopenharmony_ci unsigned int flags:8; /* flags to number() */ 44762306a36Sopenharmony_ci unsigned int base:8; /* number base, 8, 10 or 16 only */ 44862306a36Sopenharmony_ci signed int precision:16; /* # of digits/chars */ 44962306a36Sopenharmony_ci} __packed; 45062306a36Sopenharmony_cistatic_assert(sizeof(struct printf_spec) == 8); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci#define FIELD_WIDTH_MAX ((1 << 23) - 1) 45362306a36Sopenharmony_ci#define PRECISION_MAX ((1 << 15) - 1) 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic noinline_for_stack 45662306a36Sopenharmony_cichar *number(char *buf, char *end, unsigned long long num, 45762306a36Sopenharmony_ci struct printf_spec spec) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci /* put_dec requires 2-byte alignment of the buffer. */ 46062306a36Sopenharmony_ci char tmp[3 * sizeof(num)] __aligned(2); 46162306a36Sopenharmony_ci char sign; 46262306a36Sopenharmony_ci char locase; 46362306a36Sopenharmony_ci int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10); 46462306a36Sopenharmony_ci int i; 46562306a36Sopenharmony_ci bool is_zero = num == 0LL; 46662306a36Sopenharmony_ci int field_width = spec.field_width; 46762306a36Sopenharmony_ci int precision = spec.precision; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* locase = 0 or 0x20. ORing digits or letters with 'locase' 47062306a36Sopenharmony_ci * produces same digits or (maybe lowercased) letters */ 47162306a36Sopenharmony_ci locase = (spec.flags & SMALL); 47262306a36Sopenharmony_ci if (spec.flags & LEFT) 47362306a36Sopenharmony_ci spec.flags &= ~ZEROPAD; 47462306a36Sopenharmony_ci sign = 0; 47562306a36Sopenharmony_ci if (spec.flags & SIGN) { 47662306a36Sopenharmony_ci if ((signed long long)num < 0) { 47762306a36Sopenharmony_ci sign = '-'; 47862306a36Sopenharmony_ci num = -(signed long long)num; 47962306a36Sopenharmony_ci field_width--; 48062306a36Sopenharmony_ci } else if (spec.flags & PLUS) { 48162306a36Sopenharmony_ci sign = '+'; 48262306a36Sopenharmony_ci field_width--; 48362306a36Sopenharmony_ci } else if (spec.flags & SPACE) { 48462306a36Sopenharmony_ci sign = ' '; 48562306a36Sopenharmony_ci field_width--; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci if (need_pfx) { 48962306a36Sopenharmony_ci if (spec.base == 16) 49062306a36Sopenharmony_ci field_width -= 2; 49162306a36Sopenharmony_ci else if (!is_zero) 49262306a36Sopenharmony_ci field_width--; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* generate full string in tmp[], in reverse order */ 49662306a36Sopenharmony_ci i = 0; 49762306a36Sopenharmony_ci if (num < spec.base) 49862306a36Sopenharmony_ci tmp[i++] = hex_asc_upper[num] | locase; 49962306a36Sopenharmony_ci else if (spec.base != 10) { /* 8 or 16 */ 50062306a36Sopenharmony_ci int mask = spec.base - 1; 50162306a36Sopenharmony_ci int shift = 3; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (spec.base == 16) 50462306a36Sopenharmony_ci shift = 4; 50562306a36Sopenharmony_ci do { 50662306a36Sopenharmony_ci tmp[i++] = (hex_asc_upper[((unsigned char)num) & mask] | locase); 50762306a36Sopenharmony_ci num >>= shift; 50862306a36Sopenharmony_ci } while (num); 50962306a36Sopenharmony_ci } else { /* base 10 */ 51062306a36Sopenharmony_ci i = put_dec(tmp, num) - tmp; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* printing 100 using %2d gives "100", not "00" */ 51462306a36Sopenharmony_ci if (i > precision) 51562306a36Sopenharmony_ci precision = i; 51662306a36Sopenharmony_ci /* leading space padding */ 51762306a36Sopenharmony_ci field_width -= precision; 51862306a36Sopenharmony_ci if (!(spec.flags & (ZEROPAD | LEFT))) { 51962306a36Sopenharmony_ci while (--field_width >= 0) { 52062306a36Sopenharmony_ci if (buf < end) 52162306a36Sopenharmony_ci *buf = ' '; 52262306a36Sopenharmony_ci ++buf; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci /* sign */ 52662306a36Sopenharmony_ci if (sign) { 52762306a36Sopenharmony_ci if (buf < end) 52862306a36Sopenharmony_ci *buf = sign; 52962306a36Sopenharmony_ci ++buf; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci /* "0x" / "0" prefix */ 53262306a36Sopenharmony_ci if (need_pfx) { 53362306a36Sopenharmony_ci if (spec.base == 16 || !is_zero) { 53462306a36Sopenharmony_ci if (buf < end) 53562306a36Sopenharmony_ci *buf = '0'; 53662306a36Sopenharmony_ci ++buf; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci if (spec.base == 16) { 53962306a36Sopenharmony_ci if (buf < end) 54062306a36Sopenharmony_ci *buf = ('X' | locase); 54162306a36Sopenharmony_ci ++buf; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci /* zero or space padding */ 54562306a36Sopenharmony_ci if (!(spec.flags & LEFT)) { 54662306a36Sopenharmony_ci char c = ' ' + (spec.flags & ZEROPAD); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci while (--field_width >= 0) { 54962306a36Sopenharmony_ci if (buf < end) 55062306a36Sopenharmony_ci *buf = c; 55162306a36Sopenharmony_ci ++buf; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci /* hmm even more zero padding? */ 55562306a36Sopenharmony_ci while (i <= --precision) { 55662306a36Sopenharmony_ci if (buf < end) 55762306a36Sopenharmony_ci *buf = '0'; 55862306a36Sopenharmony_ci ++buf; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci /* actual digits of result */ 56162306a36Sopenharmony_ci while (--i >= 0) { 56262306a36Sopenharmony_ci if (buf < end) 56362306a36Sopenharmony_ci *buf = tmp[i]; 56462306a36Sopenharmony_ci ++buf; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci /* trailing space padding */ 56762306a36Sopenharmony_ci while (--field_width >= 0) { 56862306a36Sopenharmony_ci if (buf < end) 56962306a36Sopenharmony_ci *buf = ' '; 57062306a36Sopenharmony_ci ++buf; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return buf; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic noinline_for_stack 57762306a36Sopenharmony_cichar *special_hex_number(char *buf, char *end, unsigned long long num, int size) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct printf_spec spec; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci spec.type = FORMAT_TYPE_PTR; 58262306a36Sopenharmony_ci spec.field_width = 2 + 2 * size; /* 0x + hex */ 58362306a36Sopenharmony_ci spec.flags = SPECIAL | SMALL | ZEROPAD; 58462306a36Sopenharmony_ci spec.base = 16; 58562306a36Sopenharmony_ci spec.precision = -1; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return number(buf, end, num, spec); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void move_right(char *buf, char *end, unsigned len, unsigned spaces) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci size_t size; 59362306a36Sopenharmony_ci if (buf >= end) /* nowhere to put anything */ 59462306a36Sopenharmony_ci return; 59562306a36Sopenharmony_ci size = end - buf; 59662306a36Sopenharmony_ci if (size <= spaces) { 59762306a36Sopenharmony_ci memset(buf, ' ', size); 59862306a36Sopenharmony_ci return; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci if (len) { 60162306a36Sopenharmony_ci if (len > size - spaces) 60262306a36Sopenharmony_ci len = size - spaces; 60362306a36Sopenharmony_ci memmove(buf + spaces, buf, len); 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci memset(buf, ' ', spaces); 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/* 60962306a36Sopenharmony_ci * Handle field width padding for a string. 61062306a36Sopenharmony_ci * @buf: current buffer position 61162306a36Sopenharmony_ci * @n: length of string 61262306a36Sopenharmony_ci * @end: end of output buffer 61362306a36Sopenharmony_ci * @spec: for field width and flags 61462306a36Sopenharmony_ci * Returns: new buffer position after padding. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_cistatic noinline_for_stack 61762306a36Sopenharmony_cichar *widen_string(char *buf, int n, char *end, struct printf_spec spec) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci unsigned spaces; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (likely(n >= spec.field_width)) 62262306a36Sopenharmony_ci return buf; 62362306a36Sopenharmony_ci /* we want to pad the sucker */ 62462306a36Sopenharmony_ci spaces = spec.field_width - n; 62562306a36Sopenharmony_ci if (!(spec.flags & LEFT)) { 62662306a36Sopenharmony_ci move_right(buf - n, end, n, spaces); 62762306a36Sopenharmony_ci return buf + spaces; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci while (spaces--) { 63062306a36Sopenharmony_ci if (buf < end) 63162306a36Sopenharmony_ci *buf = ' '; 63262306a36Sopenharmony_ci ++buf; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci return buf; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* Handle string from a well known address. */ 63862306a36Sopenharmony_cistatic char *string_nocheck(char *buf, char *end, const char *s, 63962306a36Sopenharmony_ci struct printf_spec spec) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci int len = 0; 64262306a36Sopenharmony_ci int lim = spec.precision; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci while (lim--) { 64562306a36Sopenharmony_ci char c = *s++; 64662306a36Sopenharmony_ci if (!c) 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci if (buf < end) 64962306a36Sopenharmony_ci *buf = c; 65062306a36Sopenharmony_ci ++buf; 65162306a36Sopenharmony_ci ++len; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci return widen_string(buf, len, end, spec); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic char *err_ptr(char *buf, char *end, void *ptr, 65762306a36Sopenharmony_ci struct printf_spec spec) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci int err = PTR_ERR(ptr); 66062306a36Sopenharmony_ci const char *sym = errname(err); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (sym) 66362306a36Sopenharmony_ci return string_nocheck(buf, end, sym, spec); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * Somebody passed ERR_PTR(-1234) or some other non-existing 66762306a36Sopenharmony_ci * Efoo - or perhaps CONFIG_SYMBOLIC_ERRNAME=n. Fall back to 66862306a36Sopenharmony_ci * printing it as its decimal representation. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci spec.flags |= SIGN; 67162306a36Sopenharmony_ci spec.base = 10; 67262306a36Sopenharmony_ci return number(buf, end, err, spec); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci/* Be careful: error messages must fit into the given buffer. */ 67662306a36Sopenharmony_cistatic char *error_string(char *buf, char *end, const char *s, 67762306a36Sopenharmony_ci struct printf_spec spec) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci /* 68062306a36Sopenharmony_ci * Hard limit to avoid a completely insane messages. It actually 68162306a36Sopenharmony_ci * works pretty well because most error messages are in 68262306a36Sopenharmony_ci * the many pointer format modifiers. 68362306a36Sopenharmony_ci */ 68462306a36Sopenharmony_ci if (spec.precision == -1) 68562306a36Sopenharmony_ci spec.precision = 2 * sizeof(void *); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return string_nocheck(buf, end, s, spec); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci/* 69162306a36Sopenharmony_ci * Do not call any complex external code here. Nested printk()/vsprintf() 69262306a36Sopenharmony_ci * might cause infinite loops. Failures might break printk() and would 69362306a36Sopenharmony_ci * be hard to debug. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_cistatic const char *check_pointer_msg(const void *ptr) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci if (!ptr) 69862306a36Sopenharmony_ci return "(null)"; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if ((unsigned long)ptr < PAGE_SIZE || IS_ERR_VALUE(ptr)) 70162306a36Sopenharmony_ci return "(efault)"; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return NULL; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int check_pointer(char **buf, char *end, const void *ptr, 70762306a36Sopenharmony_ci struct printf_spec spec) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci const char *err_msg; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci err_msg = check_pointer_msg(ptr); 71262306a36Sopenharmony_ci if (err_msg) { 71362306a36Sopenharmony_ci *buf = error_string(*buf, end, err_msg, spec); 71462306a36Sopenharmony_ci return -EFAULT; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic noinline_for_stack 72162306a36Sopenharmony_cichar *string(char *buf, char *end, const char *s, 72262306a36Sopenharmony_ci struct printf_spec spec) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci if (check_pointer(&buf, end, s, spec)) 72562306a36Sopenharmony_ci return buf; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return string_nocheck(buf, end, s, spec); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic char *pointer_string(char *buf, char *end, 73162306a36Sopenharmony_ci const void *ptr, 73262306a36Sopenharmony_ci struct printf_spec spec) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci spec.base = 16; 73562306a36Sopenharmony_ci spec.flags |= SMALL; 73662306a36Sopenharmony_ci if (spec.field_width == -1) { 73762306a36Sopenharmony_ci spec.field_width = 2 * sizeof(ptr); 73862306a36Sopenharmony_ci spec.flags |= ZEROPAD; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return number(buf, end, (unsigned long int)ptr, spec); 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci/* Make pointers available for printing early in the boot sequence. */ 74562306a36Sopenharmony_cistatic int debug_boot_weak_hash __ro_after_init; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic int __init debug_boot_weak_hash_enable(char *str) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci debug_boot_weak_hash = 1; 75062306a36Sopenharmony_ci pr_info("debug_boot_weak_hash enabled\n"); 75162306a36Sopenharmony_ci return 0; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ciearly_param("debug_boot_weak_hash", debug_boot_weak_hash_enable); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic bool filled_random_ptr_key __read_mostly; 75662306a36Sopenharmony_cistatic siphash_key_t ptr_key __read_mostly; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int fill_ptr_key(struct notifier_block *nb, unsigned long action, void *data) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci get_random_bytes(&ptr_key, sizeof(ptr_key)); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Pairs with smp_rmb() before reading ptr_key. */ 76362306a36Sopenharmony_ci smp_wmb(); 76462306a36Sopenharmony_ci WRITE_ONCE(filled_random_ptr_key, true); 76562306a36Sopenharmony_ci return NOTIFY_DONE; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int __init vsprintf_init_hashval(void) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci static struct notifier_block fill_ptr_key_nb = { .notifier_call = fill_ptr_key }; 77162306a36Sopenharmony_ci execute_with_initialized_rng(&fill_ptr_key_nb); 77262306a36Sopenharmony_ci return 0; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_cisubsys_initcall(vsprintf_init_hashval) 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci/* Maps a pointer to a 32 bit unique identifier. */ 77762306a36Sopenharmony_cistatic inline int __ptr_to_hashval(const void *ptr, unsigned long *hashval_out) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci unsigned long hashval; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (!READ_ONCE(filled_random_ptr_key)) 78262306a36Sopenharmony_ci return -EBUSY; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Pairs with smp_wmb() after writing ptr_key. */ 78562306a36Sopenharmony_ci smp_rmb(); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci#ifdef CONFIG_64BIT 78862306a36Sopenharmony_ci hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key); 78962306a36Sopenharmony_ci /* 79062306a36Sopenharmony_ci * Mask off the first 32 bits, this makes explicit that we have 79162306a36Sopenharmony_ci * modified the address (and 32 bits is plenty for a unique ID). 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci hashval = hashval & 0xffffffff; 79462306a36Sopenharmony_ci#else 79562306a36Sopenharmony_ci hashval = (unsigned long)siphash_1u32((u32)ptr, &ptr_key); 79662306a36Sopenharmony_ci#endif 79762306a36Sopenharmony_ci *hashval_out = hashval; 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ciint ptr_to_hashval(const void *ptr, unsigned long *hashval_out) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci return __ptr_to_hashval(ptr, hashval_out); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic char *ptr_to_id(char *buf, char *end, const void *ptr, 80762306a36Sopenharmony_ci struct printf_spec spec) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci const char *str = sizeof(ptr) == 8 ? "(____ptrval____)" : "(ptrval)"; 81062306a36Sopenharmony_ci unsigned long hashval; 81162306a36Sopenharmony_ci int ret; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* 81462306a36Sopenharmony_ci * Print the real pointer value for NULL and error pointers, 81562306a36Sopenharmony_ci * as they are not actual addresses. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_ci if (IS_ERR_OR_NULL(ptr)) 81862306a36Sopenharmony_ci return pointer_string(buf, end, ptr, spec); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* When debugging early boot use non-cryptographically secure hash. */ 82162306a36Sopenharmony_ci if (unlikely(debug_boot_weak_hash)) { 82262306a36Sopenharmony_ci hashval = hash_long((unsigned long)ptr, 32); 82362306a36Sopenharmony_ci return pointer_string(buf, end, (const void *)hashval, spec); 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci ret = __ptr_to_hashval(ptr, &hashval); 82762306a36Sopenharmony_ci if (ret) { 82862306a36Sopenharmony_ci spec.field_width = 2 * sizeof(ptr); 82962306a36Sopenharmony_ci /* string length must be less than default_width */ 83062306a36Sopenharmony_ci return error_string(buf, end, str, spec); 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci return pointer_string(buf, end, (const void *)hashval, spec); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic char *default_pointer(char *buf, char *end, const void *ptr, 83762306a36Sopenharmony_ci struct printf_spec spec) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci /* 84062306a36Sopenharmony_ci * default is to _not_ leak addresses, so hash before printing, 84162306a36Sopenharmony_ci * unless no_hash_pointers is specified on the command line. 84262306a36Sopenharmony_ci */ 84362306a36Sopenharmony_ci if (unlikely(no_hash_pointers)) 84462306a36Sopenharmony_ci return pointer_string(buf, end, ptr, spec); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return ptr_to_id(buf, end, ptr, spec); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ciint kptr_restrict __read_mostly; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic noinline_for_stack 85262306a36Sopenharmony_cichar *restricted_pointer(char *buf, char *end, const void *ptr, 85362306a36Sopenharmony_ci struct printf_spec spec) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci switch (kptr_restrict) { 85662306a36Sopenharmony_ci case 0: 85762306a36Sopenharmony_ci /* Handle as %p, hash and do _not_ leak addresses. */ 85862306a36Sopenharmony_ci return default_pointer(buf, end, ptr, spec); 85962306a36Sopenharmony_ci case 1: { 86062306a36Sopenharmony_ci const struct cred *cred; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* 86362306a36Sopenharmony_ci * kptr_restrict==1 cannot be used in IRQ context 86462306a36Sopenharmony_ci * because its test for CAP_SYSLOG would be meaningless. 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci if (in_hardirq() || in_serving_softirq() || in_nmi()) { 86762306a36Sopenharmony_ci if (spec.field_width == -1) 86862306a36Sopenharmony_ci spec.field_width = 2 * sizeof(ptr); 86962306a36Sopenharmony_ci return error_string(buf, end, "pK-error", spec); 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* 87362306a36Sopenharmony_ci * Only print the real pointer value if the current 87462306a36Sopenharmony_ci * process has CAP_SYSLOG and is running with the 87562306a36Sopenharmony_ci * same credentials it started with. This is because 87662306a36Sopenharmony_ci * access to files is checked at open() time, but %pK 87762306a36Sopenharmony_ci * checks permission at read() time. We don't want to 87862306a36Sopenharmony_ci * leak pointer values if a binary opens a file using 87962306a36Sopenharmony_ci * %pK and then elevates privileges before reading it. 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_ci cred = current_cred(); 88262306a36Sopenharmony_ci if (!has_capability_noaudit(current, CAP_SYSLOG) || 88362306a36Sopenharmony_ci !uid_eq(cred->euid, cred->uid) || 88462306a36Sopenharmony_ci !gid_eq(cred->egid, cred->gid)) 88562306a36Sopenharmony_ci ptr = NULL; 88662306a36Sopenharmony_ci break; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci case 2: 88962306a36Sopenharmony_ci default: 89062306a36Sopenharmony_ci /* Always print 0's for %pK */ 89162306a36Sopenharmony_ci ptr = NULL; 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return pointer_string(buf, end, ptr, spec); 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic noinline_for_stack 89962306a36Sopenharmony_cichar *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_spec spec, 90062306a36Sopenharmony_ci const char *fmt) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci const char *array[4], *s; 90362306a36Sopenharmony_ci const struct dentry *p; 90462306a36Sopenharmony_ci int depth; 90562306a36Sopenharmony_ci int i, n; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci switch (fmt[1]) { 90862306a36Sopenharmony_ci case '2': case '3': case '4': 90962306a36Sopenharmony_ci depth = fmt[1] - '0'; 91062306a36Sopenharmony_ci break; 91162306a36Sopenharmony_ci default: 91262306a36Sopenharmony_ci depth = 1; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci rcu_read_lock(); 91662306a36Sopenharmony_ci for (i = 0; i < depth; i++, d = p) { 91762306a36Sopenharmony_ci if (check_pointer(&buf, end, d, spec)) { 91862306a36Sopenharmony_ci rcu_read_unlock(); 91962306a36Sopenharmony_ci return buf; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci p = READ_ONCE(d->d_parent); 92362306a36Sopenharmony_ci array[i] = READ_ONCE(d->d_name.name); 92462306a36Sopenharmony_ci if (p == d) { 92562306a36Sopenharmony_ci if (i) 92662306a36Sopenharmony_ci array[i] = ""; 92762306a36Sopenharmony_ci i++; 92862306a36Sopenharmony_ci break; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci s = array[--i]; 93262306a36Sopenharmony_ci for (n = 0; n != spec.precision; n++, buf++) { 93362306a36Sopenharmony_ci char c = *s++; 93462306a36Sopenharmony_ci if (!c) { 93562306a36Sopenharmony_ci if (!i) 93662306a36Sopenharmony_ci break; 93762306a36Sopenharmony_ci c = '/'; 93862306a36Sopenharmony_ci s = array[--i]; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci if (buf < end) 94162306a36Sopenharmony_ci *buf = c; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci rcu_read_unlock(); 94462306a36Sopenharmony_ci return widen_string(buf, n, end, spec); 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic noinline_for_stack 94862306a36Sopenharmony_cichar *file_dentry_name(char *buf, char *end, const struct file *f, 94962306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci if (check_pointer(&buf, end, f, spec)) 95262306a36Sopenharmony_ci return buf; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return dentry_name(buf, end, f->f_path.dentry, spec, fmt); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci#ifdef CONFIG_BLOCK 95762306a36Sopenharmony_cistatic noinline_for_stack 95862306a36Sopenharmony_cichar *bdev_name(char *buf, char *end, struct block_device *bdev, 95962306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci struct gendisk *hd; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (check_pointer(&buf, end, bdev, spec)) 96462306a36Sopenharmony_ci return buf; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci hd = bdev->bd_disk; 96762306a36Sopenharmony_ci buf = string(buf, end, hd->disk_name, spec); 96862306a36Sopenharmony_ci if (bdev->bd_partno) { 96962306a36Sopenharmony_ci if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) { 97062306a36Sopenharmony_ci if (buf < end) 97162306a36Sopenharmony_ci *buf = 'p'; 97262306a36Sopenharmony_ci buf++; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci buf = number(buf, end, bdev->bd_partno, spec); 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci return buf; 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci#endif 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic noinline_for_stack 98162306a36Sopenharmony_cichar *symbol_string(char *buf, char *end, void *ptr, 98262306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci unsigned long value; 98562306a36Sopenharmony_ci#ifdef CONFIG_KALLSYMS 98662306a36Sopenharmony_ci char sym[KSYM_SYMBOL_LEN]; 98762306a36Sopenharmony_ci#endif 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (fmt[1] == 'R') 99062306a36Sopenharmony_ci ptr = __builtin_extract_return_addr(ptr); 99162306a36Sopenharmony_ci value = (unsigned long)ptr; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci#ifdef CONFIG_KALLSYMS 99462306a36Sopenharmony_ci if (*fmt == 'B' && fmt[1] == 'b') 99562306a36Sopenharmony_ci sprint_backtrace_build_id(sym, value); 99662306a36Sopenharmony_ci else if (*fmt == 'B') 99762306a36Sopenharmony_ci sprint_backtrace(sym, value); 99862306a36Sopenharmony_ci else if (*fmt == 'S' && (fmt[1] == 'b' || (fmt[1] == 'R' && fmt[2] == 'b'))) 99962306a36Sopenharmony_ci sprint_symbol_build_id(sym, value); 100062306a36Sopenharmony_ci else if (*fmt != 's') 100162306a36Sopenharmony_ci sprint_symbol(sym, value); 100262306a36Sopenharmony_ci else 100362306a36Sopenharmony_ci sprint_symbol_no_offset(sym, value); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return string_nocheck(buf, end, sym, spec); 100662306a36Sopenharmony_ci#else 100762306a36Sopenharmony_ci return special_hex_number(buf, end, value, sizeof(void *)); 100862306a36Sopenharmony_ci#endif 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic const struct printf_spec default_str_spec = { 101262306a36Sopenharmony_ci .field_width = -1, 101362306a36Sopenharmony_ci .precision = -1, 101462306a36Sopenharmony_ci}; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic const struct printf_spec default_flag_spec = { 101762306a36Sopenharmony_ci .base = 16, 101862306a36Sopenharmony_ci .precision = -1, 101962306a36Sopenharmony_ci .flags = SPECIAL | SMALL, 102062306a36Sopenharmony_ci}; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic const struct printf_spec default_dec_spec = { 102362306a36Sopenharmony_ci .base = 10, 102462306a36Sopenharmony_ci .precision = -1, 102562306a36Sopenharmony_ci}; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic const struct printf_spec default_dec02_spec = { 102862306a36Sopenharmony_ci .base = 10, 102962306a36Sopenharmony_ci .field_width = 2, 103062306a36Sopenharmony_ci .precision = -1, 103162306a36Sopenharmony_ci .flags = ZEROPAD, 103262306a36Sopenharmony_ci}; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic const struct printf_spec default_dec04_spec = { 103562306a36Sopenharmony_ci .base = 10, 103662306a36Sopenharmony_ci .field_width = 4, 103762306a36Sopenharmony_ci .precision = -1, 103862306a36Sopenharmony_ci .flags = ZEROPAD, 103962306a36Sopenharmony_ci}; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic noinline_for_stack 104262306a36Sopenharmony_cichar *resource_string(char *buf, char *end, struct resource *res, 104362306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci#ifndef IO_RSRC_PRINTK_SIZE 104662306a36Sopenharmony_ci#define IO_RSRC_PRINTK_SIZE 6 104762306a36Sopenharmony_ci#endif 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci#ifndef MEM_RSRC_PRINTK_SIZE 105062306a36Sopenharmony_ci#define MEM_RSRC_PRINTK_SIZE 10 105162306a36Sopenharmony_ci#endif 105262306a36Sopenharmony_ci static const struct printf_spec io_spec = { 105362306a36Sopenharmony_ci .base = 16, 105462306a36Sopenharmony_ci .field_width = IO_RSRC_PRINTK_SIZE, 105562306a36Sopenharmony_ci .precision = -1, 105662306a36Sopenharmony_ci .flags = SPECIAL | SMALL | ZEROPAD, 105762306a36Sopenharmony_ci }; 105862306a36Sopenharmony_ci static const struct printf_spec mem_spec = { 105962306a36Sopenharmony_ci .base = 16, 106062306a36Sopenharmony_ci .field_width = MEM_RSRC_PRINTK_SIZE, 106162306a36Sopenharmony_ci .precision = -1, 106262306a36Sopenharmony_ci .flags = SPECIAL | SMALL | ZEROPAD, 106362306a36Sopenharmony_ci }; 106462306a36Sopenharmony_ci static const struct printf_spec bus_spec = { 106562306a36Sopenharmony_ci .base = 16, 106662306a36Sopenharmony_ci .field_width = 2, 106762306a36Sopenharmony_ci .precision = -1, 106862306a36Sopenharmony_ci .flags = SMALL | ZEROPAD, 106962306a36Sopenharmony_ci }; 107062306a36Sopenharmony_ci static const struct printf_spec str_spec = { 107162306a36Sopenharmony_ci .field_width = -1, 107262306a36Sopenharmony_ci .precision = 10, 107362306a36Sopenharmony_ci .flags = LEFT, 107462306a36Sopenharmony_ci }; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* 32-bit res (sizeof==4): 10 chars in dec, 10 in hex ("0x" + 8) 107762306a36Sopenharmony_ci * 64-bit res (sizeof==8): 20 chars in dec, 18 in hex ("0x" + 16) */ 107862306a36Sopenharmony_ci#define RSRC_BUF_SIZE ((2 * sizeof(resource_size_t)) + 4) 107962306a36Sopenharmony_ci#define FLAG_BUF_SIZE (2 * sizeof(res->flags)) 108062306a36Sopenharmony_ci#define DECODED_BUF_SIZE sizeof("[mem - 64bit pref window disabled]") 108162306a36Sopenharmony_ci#define RAW_BUF_SIZE sizeof("[mem - flags 0x]") 108262306a36Sopenharmony_ci char sym[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE, 108362306a36Sopenharmony_ci 2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)]; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci char *p = sym, *pend = sym + sizeof(sym); 108662306a36Sopenharmony_ci int decode = (fmt[0] == 'R') ? 1 : 0; 108762306a36Sopenharmony_ci const struct printf_spec *specp; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (check_pointer(&buf, end, res, spec)) 109062306a36Sopenharmony_ci return buf; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci *p++ = '['; 109362306a36Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 109462306a36Sopenharmony_ci p = string_nocheck(p, pend, "io ", str_spec); 109562306a36Sopenharmony_ci specp = &io_spec; 109662306a36Sopenharmony_ci } else if (res->flags & IORESOURCE_MEM) { 109762306a36Sopenharmony_ci p = string_nocheck(p, pend, "mem ", str_spec); 109862306a36Sopenharmony_ci specp = &mem_spec; 109962306a36Sopenharmony_ci } else if (res->flags & IORESOURCE_IRQ) { 110062306a36Sopenharmony_ci p = string_nocheck(p, pend, "irq ", str_spec); 110162306a36Sopenharmony_ci specp = &default_dec_spec; 110262306a36Sopenharmony_ci } else if (res->flags & IORESOURCE_DMA) { 110362306a36Sopenharmony_ci p = string_nocheck(p, pend, "dma ", str_spec); 110462306a36Sopenharmony_ci specp = &default_dec_spec; 110562306a36Sopenharmony_ci } else if (res->flags & IORESOURCE_BUS) { 110662306a36Sopenharmony_ci p = string_nocheck(p, pend, "bus ", str_spec); 110762306a36Sopenharmony_ci specp = &bus_spec; 110862306a36Sopenharmony_ci } else { 110962306a36Sopenharmony_ci p = string_nocheck(p, pend, "??? ", str_spec); 111062306a36Sopenharmony_ci specp = &mem_spec; 111162306a36Sopenharmony_ci decode = 0; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci if (decode && res->flags & IORESOURCE_UNSET) { 111462306a36Sopenharmony_ci p = string_nocheck(p, pend, "size ", str_spec); 111562306a36Sopenharmony_ci p = number(p, pend, resource_size(res), *specp); 111662306a36Sopenharmony_ci } else { 111762306a36Sopenharmony_ci p = number(p, pend, res->start, *specp); 111862306a36Sopenharmony_ci if (res->start != res->end) { 111962306a36Sopenharmony_ci *p++ = '-'; 112062306a36Sopenharmony_ci p = number(p, pend, res->end, *specp); 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci if (decode) { 112462306a36Sopenharmony_ci if (res->flags & IORESOURCE_MEM_64) 112562306a36Sopenharmony_ci p = string_nocheck(p, pend, " 64bit", str_spec); 112662306a36Sopenharmony_ci if (res->flags & IORESOURCE_PREFETCH) 112762306a36Sopenharmony_ci p = string_nocheck(p, pend, " pref", str_spec); 112862306a36Sopenharmony_ci if (res->flags & IORESOURCE_WINDOW) 112962306a36Sopenharmony_ci p = string_nocheck(p, pend, " window", str_spec); 113062306a36Sopenharmony_ci if (res->flags & IORESOURCE_DISABLED) 113162306a36Sopenharmony_ci p = string_nocheck(p, pend, " disabled", str_spec); 113262306a36Sopenharmony_ci } else { 113362306a36Sopenharmony_ci p = string_nocheck(p, pend, " flags ", str_spec); 113462306a36Sopenharmony_ci p = number(p, pend, res->flags, default_flag_spec); 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci *p++ = ']'; 113762306a36Sopenharmony_ci *p = '\0'; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci return string_nocheck(buf, end, sym, spec); 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic noinline_for_stack 114362306a36Sopenharmony_cichar *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec, 114462306a36Sopenharmony_ci const char *fmt) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci int i, len = 1; /* if we pass '%ph[CDN]', field width remains 114762306a36Sopenharmony_ci negative value, fallback to the default */ 114862306a36Sopenharmony_ci char separator; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (spec.field_width == 0) 115162306a36Sopenharmony_ci /* nothing to print */ 115262306a36Sopenharmony_ci return buf; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (check_pointer(&buf, end, addr, spec)) 115562306a36Sopenharmony_ci return buf; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci switch (fmt[1]) { 115862306a36Sopenharmony_ci case 'C': 115962306a36Sopenharmony_ci separator = ':'; 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci case 'D': 116262306a36Sopenharmony_ci separator = '-'; 116362306a36Sopenharmony_ci break; 116462306a36Sopenharmony_ci case 'N': 116562306a36Sopenharmony_ci separator = 0; 116662306a36Sopenharmony_ci break; 116762306a36Sopenharmony_ci default: 116862306a36Sopenharmony_ci separator = ' '; 116962306a36Sopenharmony_ci break; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (spec.field_width > 0) 117362306a36Sopenharmony_ci len = min_t(int, spec.field_width, 64); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci for (i = 0; i < len; ++i) { 117662306a36Sopenharmony_ci if (buf < end) 117762306a36Sopenharmony_ci *buf = hex_asc_hi(addr[i]); 117862306a36Sopenharmony_ci ++buf; 117962306a36Sopenharmony_ci if (buf < end) 118062306a36Sopenharmony_ci *buf = hex_asc_lo(addr[i]); 118162306a36Sopenharmony_ci ++buf; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (separator && i != len - 1) { 118462306a36Sopenharmony_ci if (buf < end) 118562306a36Sopenharmony_ci *buf = separator; 118662306a36Sopenharmony_ci ++buf; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci return buf; 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic noinline_for_stack 119462306a36Sopenharmony_cichar *bitmap_string(char *buf, char *end, const unsigned long *bitmap, 119562306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci const int CHUNKSZ = 32; 119862306a36Sopenharmony_ci int nr_bits = max_t(int, spec.field_width, 0); 119962306a36Sopenharmony_ci int i, chunksz; 120062306a36Sopenharmony_ci bool first = true; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (check_pointer(&buf, end, bitmap, spec)) 120362306a36Sopenharmony_ci return buf; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* reused to print numbers */ 120662306a36Sopenharmony_ci spec = (struct printf_spec){ .flags = SMALL | ZEROPAD, .base = 16 }; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci chunksz = nr_bits & (CHUNKSZ - 1); 120962306a36Sopenharmony_ci if (chunksz == 0) 121062306a36Sopenharmony_ci chunksz = CHUNKSZ; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci i = ALIGN(nr_bits, CHUNKSZ) - CHUNKSZ; 121362306a36Sopenharmony_ci for (; i >= 0; i -= CHUNKSZ) { 121462306a36Sopenharmony_ci u32 chunkmask, val; 121562306a36Sopenharmony_ci int word, bit; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci chunkmask = ((1ULL << chunksz) - 1); 121862306a36Sopenharmony_ci word = i / BITS_PER_LONG; 121962306a36Sopenharmony_ci bit = i % BITS_PER_LONG; 122062306a36Sopenharmony_ci val = (bitmap[word] >> bit) & chunkmask; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (!first) { 122362306a36Sopenharmony_ci if (buf < end) 122462306a36Sopenharmony_ci *buf = ','; 122562306a36Sopenharmony_ci buf++; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci first = false; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci spec.field_width = DIV_ROUND_UP(chunksz, 4); 123062306a36Sopenharmony_ci buf = number(buf, end, val, spec); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci chunksz = CHUNKSZ; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci return buf; 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic noinline_for_stack 123862306a36Sopenharmony_cichar *bitmap_list_string(char *buf, char *end, const unsigned long *bitmap, 123962306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci int nr_bits = max_t(int, spec.field_width, 0); 124262306a36Sopenharmony_ci bool first = true; 124362306a36Sopenharmony_ci int rbot, rtop; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (check_pointer(&buf, end, bitmap, spec)) 124662306a36Sopenharmony_ci return buf; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci for_each_set_bitrange(rbot, rtop, bitmap, nr_bits) { 124962306a36Sopenharmony_ci if (!first) { 125062306a36Sopenharmony_ci if (buf < end) 125162306a36Sopenharmony_ci *buf = ','; 125262306a36Sopenharmony_ci buf++; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci first = false; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci buf = number(buf, end, rbot, default_dec_spec); 125762306a36Sopenharmony_ci if (rtop == rbot + 1) 125862306a36Sopenharmony_ci continue; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci if (buf < end) 126162306a36Sopenharmony_ci *buf = '-'; 126262306a36Sopenharmony_ci buf = number(++buf, end, rtop - 1, default_dec_spec); 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci return buf; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic noinline_for_stack 126862306a36Sopenharmony_cichar *mac_address_string(char *buf, char *end, u8 *addr, 126962306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci char mac_addr[sizeof("xx:xx:xx:xx:xx:xx")]; 127262306a36Sopenharmony_ci char *p = mac_addr; 127362306a36Sopenharmony_ci int i; 127462306a36Sopenharmony_ci char separator; 127562306a36Sopenharmony_ci bool reversed = false; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (check_pointer(&buf, end, addr, spec)) 127862306a36Sopenharmony_ci return buf; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci switch (fmt[1]) { 128162306a36Sopenharmony_ci case 'F': 128262306a36Sopenharmony_ci separator = '-'; 128362306a36Sopenharmony_ci break; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci case 'R': 128662306a36Sopenharmony_ci reversed = true; 128762306a36Sopenharmony_ci fallthrough; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci default: 129062306a36Sopenharmony_ci separator = ':'; 129162306a36Sopenharmony_ci break; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 129562306a36Sopenharmony_ci if (reversed) 129662306a36Sopenharmony_ci p = hex_byte_pack(p, addr[5 - i]); 129762306a36Sopenharmony_ci else 129862306a36Sopenharmony_ci p = hex_byte_pack(p, addr[i]); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (fmt[0] == 'M' && i != 5) 130162306a36Sopenharmony_ci *p++ = separator; 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci *p = '\0'; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci return string_nocheck(buf, end, mac_addr, spec); 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cistatic noinline_for_stack 130962306a36Sopenharmony_cichar *ip4_string(char *p, const u8 *addr, const char *fmt) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci int i; 131262306a36Sopenharmony_ci bool leading_zeros = (fmt[0] == 'i'); 131362306a36Sopenharmony_ci int index; 131462306a36Sopenharmony_ci int step; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci switch (fmt[2]) { 131762306a36Sopenharmony_ci case 'h': 131862306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 131962306a36Sopenharmony_ci index = 0; 132062306a36Sopenharmony_ci step = 1; 132162306a36Sopenharmony_ci#else 132262306a36Sopenharmony_ci index = 3; 132362306a36Sopenharmony_ci step = -1; 132462306a36Sopenharmony_ci#endif 132562306a36Sopenharmony_ci break; 132662306a36Sopenharmony_ci case 'l': 132762306a36Sopenharmony_ci index = 3; 132862306a36Sopenharmony_ci step = -1; 132962306a36Sopenharmony_ci break; 133062306a36Sopenharmony_ci case 'n': 133162306a36Sopenharmony_ci case 'b': 133262306a36Sopenharmony_ci default: 133362306a36Sopenharmony_ci index = 0; 133462306a36Sopenharmony_ci step = 1; 133562306a36Sopenharmony_ci break; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 133862306a36Sopenharmony_ci char temp[4] __aligned(2); /* hold each IP quad in reverse order */ 133962306a36Sopenharmony_ci int digits = put_dec_trunc8(temp, addr[index]) - temp; 134062306a36Sopenharmony_ci if (leading_zeros) { 134162306a36Sopenharmony_ci if (digits < 3) 134262306a36Sopenharmony_ci *p++ = '0'; 134362306a36Sopenharmony_ci if (digits < 2) 134462306a36Sopenharmony_ci *p++ = '0'; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci /* reverse the digits in the quad */ 134762306a36Sopenharmony_ci while (digits--) 134862306a36Sopenharmony_ci *p++ = temp[digits]; 134962306a36Sopenharmony_ci if (i < 3) 135062306a36Sopenharmony_ci *p++ = '.'; 135162306a36Sopenharmony_ci index += step; 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci *p = '\0'; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return p; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic noinline_for_stack 135962306a36Sopenharmony_cichar *ip6_compressed_string(char *p, const char *addr) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci int i, j, range; 136262306a36Sopenharmony_ci unsigned char zerolength[8]; 136362306a36Sopenharmony_ci int longest = 1; 136462306a36Sopenharmony_ci int colonpos = -1; 136562306a36Sopenharmony_ci u16 word; 136662306a36Sopenharmony_ci u8 hi, lo; 136762306a36Sopenharmony_ci bool needcolon = false; 136862306a36Sopenharmony_ci bool useIPv4; 136962306a36Sopenharmony_ci struct in6_addr in6; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci memcpy(&in6, addr, sizeof(struct in6_addr)); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci memset(zerolength, 0, sizeof(zerolength)); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (useIPv4) 137862306a36Sopenharmony_ci range = 6; 137962306a36Sopenharmony_ci else 138062306a36Sopenharmony_ci range = 8; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* find position of longest 0 run */ 138362306a36Sopenharmony_ci for (i = 0; i < range; i++) { 138462306a36Sopenharmony_ci for (j = i; j < range; j++) { 138562306a36Sopenharmony_ci if (in6.s6_addr16[j] != 0) 138662306a36Sopenharmony_ci break; 138762306a36Sopenharmony_ci zerolength[i]++; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci for (i = 0; i < range; i++) { 139162306a36Sopenharmony_ci if (zerolength[i] > longest) { 139262306a36Sopenharmony_ci longest = zerolength[i]; 139362306a36Sopenharmony_ci colonpos = i; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci if (longest == 1) /* don't compress a single 0 */ 139762306a36Sopenharmony_ci colonpos = -1; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci /* emit address */ 140062306a36Sopenharmony_ci for (i = 0; i < range; i++) { 140162306a36Sopenharmony_ci if (i == colonpos) { 140262306a36Sopenharmony_ci if (needcolon || i == 0) 140362306a36Sopenharmony_ci *p++ = ':'; 140462306a36Sopenharmony_ci *p++ = ':'; 140562306a36Sopenharmony_ci needcolon = false; 140662306a36Sopenharmony_ci i += longest - 1; 140762306a36Sopenharmony_ci continue; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci if (needcolon) { 141062306a36Sopenharmony_ci *p++ = ':'; 141162306a36Sopenharmony_ci needcolon = false; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci /* hex u16 without leading 0s */ 141462306a36Sopenharmony_ci word = ntohs(in6.s6_addr16[i]); 141562306a36Sopenharmony_ci hi = word >> 8; 141662306a36Sopenharmony_ci lo = word & 0xff; 141762306a36Sopenharmony_ci if (hi) { 141862306a36Sopenharmony_ci if (hi > 0x0f) 141962306a36Sopenharmony_ci p = hex_byte_pack(p, hi); 142062306a36Sopenharmony_ci else 142162306a36Sopenharmony_ci *p++ = hex_asc_lo(hi); 142262306a36Sopenharmony_ci p = hex_byte_pack(p, lo); 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci else if (lo > 0x0f) 142562306a36Sopenharmony_ci p = hex_byte_pack(p, lo); 142662306a36Sopenharmony_ci else 142762306a36Sopenharmony_ci *p++ = hex_asc_lo(lo); 142862306a36Sopenharmony_ci needcolon = true; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (useIPv4) { 143262306a36Sopenharmony_ci if (needcolon) 143362306a36Sopenharmony_ci *p++ = ':'; 143462306a36Sopenharmony_ci p = ip4_string(p, &in6.s6_addr[12], "I4"); 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci *p = '\0'; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci return p; 143962306a36Sopenharmony_ci} 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cistatic noinline_for_stack 144262306a36Sopenharmony_cichar *ip6_string(char *p, const char *addr, const char *fmt) 144362306a36Sopenharmony_ci{ 144462306a36Sopenharmony_ci int i; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 144762306a36Sopenharmony_ci p = hex_byte_pack(p, *addr++); 144862306a36Sopenharmony_ci p = hex_byte_pack(p, *addr++); 144962306a36Sopenharmony_ci if (fmt[0] == 'I' && i != 7) 145062306a36Sopenharmony_ci *p++ = ':'; 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci *p = '\0'; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci return p; 145562306a36Sopenharmony_ci} 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic noinline_for_stack 145862306a36Sopenharmony_cichar *ip6_addr_string(char *buf, char *end, const u8 *addr, 145962306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci if (fmt[0] == 'I' && fmt[2] == 'c') 146462306a36Sopenharmony_ci ip6_compressed_string(ip6_addr, addr); 146562306a36Sopenharmony_ci else 146662306a36Sopenharmony_ci ip6_string(ip6_addr, addr, fmt); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci return string_nocheck(buf, end, ip6_addr, spec); 146962306a36Sopenharmony_ci} 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_cistatic noinline_for_stack 147262306a36Sopenharmony_cichar *ip4_addr_string(char *buf, char *end, const u8 *addr, 147362306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci char ip4_addr[sizeof("255.255.255.255")]; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci ip4_string(ip4_addr, addr, fmt); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci return string_nocheck(buf, end, ip4_addr, spec); 148062306a36Sopenharmony_ci} 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_cistatic noinline_for_stack 148362306a36Sopenharmony_cichar *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa, 148462306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 148562306a36Sopenharmony_ci{ 148662306a36Sopenharmony_ci bool have_p = false, have_s = false, have_f = false, have_c = false; 148762306a36Sopenharmony_ci char ip6_addr[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") + 148862306a36Sopenharmony_ci sizeof(":12345") + sizeof("/123456789") + 148962306a36Sopenharmony_ci sizeof("%1234567890")]; 149062306a36Sopenharmony_ci char *p = ip6_addr, *pend = ip6_addr + sizeof(ip6_addr); 149162306a36Sopenharmony_ci const u8 *addr = (const u8 *) &sa->sin6_addr; 149262306a36Sopenharmony_ci char fmt6[2] = { fmt[0], '6' }; 149362306a36Sopenharmony_ci u8 off = 0; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci fmt++; 149662306a36Sopenharmony_ci while (isalpha(*++fmt)) { 149762306a36Sopenharmony_ci switch (*fmt) { 149862306a36Sopenharmony_ci case 'p': 149962306a36Sopenharmony_ci have_p = true; 150062306a36Sopenharmony_ci break; 150162306a36Sopenharmony_ci case 'f': 150262306a36Sopenharmony_ci have_f = true; 150362306a36Sopenharmony_ci break; 150462306a36Sopenharmony_ci case 's': 150562306a36Sopenharmony_ci have_s = true; 150662306a36Sopenharmony_ci break; 150762306a36Sopenharmony_ci case 'c': 150862306a36Sopenharmony_ci have_c = true; 150962306a36Sopenharmony_ci break; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci if (have_p || have_s || have_f) { 151462306a36Sopenharmony_ci *p = '['; 151562306a36Sopenharmony_ci off = 1; 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (fmt6[0] == 'I' && have_c) 151962306a36Sopenharmony_ci p = ip6_compressed_string(ip6_addr + off, addr); 152062306a36Sopenharmony_ci else 152162306a36Sopenharmony_ci p = ip6_string(ip6_addr + off, addr, fmt6); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if (have_p || have_s || have_f) 152462306a36Sopenharmony_ci *p++ = ']'; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci if (have_p) { 152762306a36Sopenharmony_ci *p++ = ':'; 152862306a36Sopenharmony_ci p = number(p, pend, ntohs(sa->sin6_port), spec); 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci if (have_f) { 153162306a36Sopenharmony_ci *p++ = '/'; 153262306a36Sopenharmony_ci p = number(p, pend, ntohl(sa->sin6_flowinfo & 153362306a36Sopenharmony_ci IPV6_FLOWINFO_MASK), spec); 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci if (have_s) { 153662306a36Sopenharmony_ci *p++ = '%'; 153762306a36Sopenharmony_ci p = number(p, pend, sa->sin6_scope_id, spec); 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci *p = '\0'; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci return string_nocheck(buf, end, ip6_addr, spec); 154262306a36Sopenharmony_ci} 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_cistatic noinline_for_stack 154562306a36Sopenharmony_cichar *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa, 154662306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci bool have_p = false; 154962306a36Sopenharmony_ci char *p, ip4_addr[sizeof("255.255.255.255") + sizeof(":12345")]; 155062306a36Sopenharmony_ci char *pend = ip4_addr + sizeof(ip4_addr); 155162306a36Sopenharmony_ci const u8 *addr = (const u8 *) &sa->sin_addr.s_addr; 155262306a36Sopenharmony_ci char fmt4[3] = { fmt[0], '4', 0 }; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci fmt++; 155562306a36Sopenharmony_ci while (isalpha(*++fmt)) { 155662306a36Sopenharmony_ci switch (*fmt) { 155762306a36Sopenharmony_ci case 'p': 155862306a36Sopenharmony_ci have_p = true; 155962306a36Sopenharmony_ci break; 156062306a36Sopenharmony_ci case 'h': 156162306a36Sopenharmony_ci case 'l': 156262306a36Sopenharmony_ci case 'n': 156362306a36Sopenharmony_ci case 'b': 156462306a36Sopenharmony_ci fmt4[2] = *fmt; 156562306a36Sopenharmony_ci break; 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci p = ip4_string(ip4_addr, addr, fmt4); 157062306a36Sopenharmony_ci if (have_p) { 157162306a36Sopenharmony_ci *p++ = ':'; 157262306a36Sopenharmony_ci p = number(p, pend, ntohs(sa->sin_port), spec); 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci *p = '\0'; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci return string_nocheck(buf, end, ip4_addr, spec); 157762306a36Sopenharmony_ci} 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_cistatic noinline_for_stack 158062306a36Sopenharmony_cichar *ip_addr_string(char *buf, char *end, const void *ptr, 158162306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci char *err_fmt_msg; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci if (check_pointer(&buf, end, ptr, spec)) 158662306a36Sopenharmony_ci return buf; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci switch (fmt[1]) { 158962306a36Sopenharmony_ci case '6': 159062306a36Sopenharmony_ci return ip6_addr_string(buf, end, ptr, spec, fmt); 159162306a36Sopenharmony_ci case '4': 159262306a36Sopenharmony_ci return ip4_addr_string(buf, end, ptr, spec, fmt); 159362306a36Sopenharmony_ci case 'S': { 159462306a36Sopenharmony_ci const union { 159562306a36Sopenharmony_ci struct sockaddr raw; 159662306a36Sopenharmony_ci struct sockaddr_in v4; 159762306a36Sopenharmony_ci struct sockaddr_in6 v6; 159862306a36Sopenharmony_ci } *sa = ptr; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci switch (sa->raw.sa_family) { 160162306a36Sopenharmony_ci case AF_INET: 160262306a36Sopenharmony_ci return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt); 160362306a36Sopenharmony_ci case AF_INET6: 160462306a36Sopenharmony_ci return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt); 160562306a36Sopenharmony_ci default: 160662306a36Sopenharmony_ci return error_string(buf, end, "(einval)", spec); 160762306a36Sopenharmony_ci }} 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)"; 161162306a36Sopenharmony_ci return error_string(buf, end, err_fmt_msg, spec); 161262306a36Sopenharmony_ci} 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_cistatic noinline_for_stack 161562306a36Sopenharmony_cichar *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec, 161662306a36Sopenharmony_ci const char *fmt) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci bool found = true; 161962306a36Sopenharmony_ci int count = 1; 162062306a36Sopenharmony_ci unsigned int flags = 0; 162162306a36Sopenharmony_ci int len; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci if (spec.field_width == 0) 162462306a36Sopenharmony_ci return buf; /* nothing to print */ 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (check_pointer(&buf, end, addr, spec)) 162762306a36Sopenharmony_ci return buf; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci do { 163062306a36Sopenharmony_ci switch (fmt[count++]) { 163162306a36Sopenharmony_ci case 'a': 163262306a36Sopenharmony_ci flags |= ESCAPE_ANY; 163362306a36Sopenharmony_ci break; 163462306a36Sopenharmony_ci case 'c': 163562306a36Sopenharmony_ci flags |= ESCAPE_SPECIAL; 163662306a36Sopenharmony_ci break; 163762306a36Sopenharmony_ci case 'h': 163862306a36Sopenharmony_ci flags |= ESCAPE_HEX; 163962306a36Sopenharmony_ci break; 164062306a36Sopenharmony_ci case 'n': 164162306a36Sopenharmony_ci flags |= ESCAPE_NULL; 164262306a36Sopenharmony_ci break; 164362306a36Sopenharmony_ci case 'o': 164462306a36Sopenharmony_ci flags |= ESCAPE_OCTAL; 164562306a36Sopenharmony_ci break; 164662306a36Sopenharmony_ci case 'p': 164762306a36Sopenharmony_ci flags |= ESCAPE_NP; 164862306a36Sopenharmony_ci break; 164962306a36Sopenharmony_ci case 's': 165062306a36Sopenharmony_ci flags |= ESCAPE_SPACE; 165162306a36Sopenharmony_ci break; 165262306a36Sopenharmony_ci default: 165362306a36Sopenharmony_ci found = false; 165462306a36Sopenharmony_ci break; 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci } while (found); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci if (!flags) 165962306a36Sopenharmony_ci flags = ESCAPE_ANY_NP; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci len = spec.field_width < 0 ? 1 : spec.field_width; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci /* 166462306a36Sopenharmony_ci * string_escape_mem() writes as many characters as it can to 166562306a36Sopenharmony_ci * the given buffer, and returns the total size of the output 166662306a36Sopenharmony_ci * had the buffer been big enough. 166762306a36Sopenharmony_ci */ 166862306a36Sopenharmony_ci buf += string_escape_mem(addr, len, buf, buf < end ? end - buf : 0, flags, NULL); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return buf; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic char *va_format(char *buf, char *end, struct va_format *va_fmt, 167462306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 167562306a36Sopenharmony_ci{ 167662306a36Sopenharmony_ci va_list va; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (check_pointer(&buf, end, va_fmt, spec)) 167962306a36Sopenharmony_ci return buf; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci va_copy(va, *va_fmt->va); 168262306a36Sopenharmony_ci buf += vsnprintf(buf, end > buf ? end - buf : 0, va_fmt->fmt, va); 168362306a36Sopenharmony_ci va_end(va); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci return buf; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic noinline_for_stack 168962306a36Sopenharmony_cichar *uuid_string(char *buf, char *end, const u8 *addr, 169062306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci char uuid[UUID_STRING_LEN + 1]; 169362306a36Sopenharmony_ci char *p = uuid; 169462306a36Sopenharmony_ci int i; 169562306a36Sopenharmony_ci const u8 *index = uuid_index; 169662306a36Sopenharmony_ci bool uc = false; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (check_pointer(&buf, end, addr, spec)) 169962306a36Sopenharmony_ci return buf; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci switch (*(++fmt)) { 170262306a36Sopenharmony_ci case 'L': 170362306a36Sopenharmony_ci uc = true; 170462306a36Sopenharmony_ci fallthrough; 170562306a36Sopenharmony_ci case 'l': 170662306a36Sopenharmony_ci index = guid_index; 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci case 'B': 170962306a36Sopenharmony_ci uc = true; 171062306a36Sopenharmony_ci break; 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 171462306a36Sopenharmony_ci if (uc) 171562306a36Sopenharmony_ci p = hex_byte_pack_upper(p, addr[index[i]]); 171662306a36Sopenharmony_ci else 171762306a36Sopenharmony_ci p = hex_byte_pack(p, addr[index[i]]); 171862306a36Sopenharmony_ci switch (i) { 171962306a36Sopenharmony_ci case 3: 172062306a36Sopenharmony_ci case 5: 172162306a36Sopenharmony_ci case 7: 172262306a36Sopenharmony_ci case 9: 172362306a36Sopenharmony_ci *p++ = '-'; 172462306a36Sopenharmony_ci break; 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci *p = 0; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci return string_nocheck(buf, end, uuid, spec); 173162306a36Sopenharmony_ci} 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_cistatic noinline_for_stack 173462306a36Sopenharmony_cichar *netdev_bits(char *buf, char *end, const void *addr, 173562306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci unsigned long long num; 173862306a36Sopenharmony_ci int size; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci if (check_pointer(&buf, end, addr, spec)) 174162306a36Sopenharmony_ci return buf; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci switch (fmt[1]) { 174462306a36Sopenharmony_ci case 'F': 174562306a36Sopenharmony_ci num = *(const netdev_features_t *)addr; 174662306a36Sopenharmony_ci size = sizeof(netdev_features_t); 174762306a36Sopenharmony_ci break; 174862306a36Sopenharmony_ci default: 174962306a36Sopenharmony_ci return error_string(buf, end, "(%pN?)", spec); 175062306a36Sopenharmony_ci } 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci return special_hex_number(buf, end, num, size); 175362306a36Sopenharmony_ci} 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_cistatic noinline_for_stack 175662306a36Sopenharmony_cichar *fourcc_string(char *buf, char *end, const u32 *fourcc, 175762306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 175862306a36Sopenharmony_ci{ 175962306a36Sopenharmony_ci char output[sizeof("0123 little-endian (0x01234567)")]; 176062306a36Sopenharmony_ci char *p = output; 176162306a36Sopenharmony_ci unsigned int i; 176262306a36Sopenharmony_ci u32 orig, val; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci if (fmt[1] != 'c' || fmt[2] != 'c') 176562306a36Sopenharmony_ci return error_string(buf, end, "(%p4?)", spec); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (check_pointer(&buf, end, fourcc, spec)) 176862306a36Sopenharmony_ci return buf; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci orig = get_unaligned(fourcc); 177162306a36Sopenharmony_ci val = orig & ~BIT(31); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci for (i = 0; i < sizeof(u32); i++) { 177462306a36Sopenharmony_ci unsigned char c = val >> (i * 8); 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci /* Print non-control ASCII characters as-is, dot otherwise */ 177762306a36Sopenharmony_ci *p++ = isascii(c) && isprint(c) ? c : '.'; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci *p++ = ' '; 178162306a36Sopenharmony_ci strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian"); 178262306a36Sopenharmony_ci p += strlen(p); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci *p++ = ' '; 178562306a36Sopenharmony_ci *p++ = '('; 178662306a36Sopenharmony_ci p = special_hex_number(p, output + sizeof(output) - 2, orig, sizeof(u32)); 178762306a36Sopenharmony_ci *p++ = ')'; 178862306a36Sopenharmony_ci *p = '\0'; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci return string(buf, end, output, spec); 179162306a36Sopenharmony_ci} 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_cistatic noinline_for_stack 179462306a36Sopenharmony_cichar *address_val(char *buf, char *end, const void *addr, 179562306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 179662306a36Sopenharmony_ci{ 179762306a36Sopenharmony_ci unsigned long long num; 179862306a36Sopenharmony_ci int size; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci if (check_pointer(&buf, end, addr, spec)) 180162306a36Sopenharmony_ci return buf; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci switch (fmt[1]) { 180462306a36Sopenharmony_ci case 'd': 180562306a36Sopenharmony_ci num = *(const dma_addr_t *)addr; 180662306a36Sopenharmony_ci size = sizeof(dma_addr_t); 180762306a36Sopenharmony_ci break; 180862306a36Sopenharmony_ci case 'p': 180962306a36Sopenharmony_ci default: 181062306a36Sopenharmony_ci num = *(const phys_addr_t *)addr; 181162306a36Sopenharmony_ci size = sizeof(phys_addr_t); 181262306a36Sopenharmony_ci break; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci return special_hex_number(buf, end, num, size); 181662306a36Sopenharmony_ci} 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_cistatic noinline_for_stack 181962306a36Sopenharmony_cichar *date_str(char *buf, char *end, const struct rtc_time *tm, bool r) 182062306a36Sopenharmony_ci{ 182162306a36Sopenharmony_ci int year = tm->tm_year + (r ? 0 : 1900); 182262306a36Sopenharmony_ci int mon = tm->tm_mon + (r ? 0 : 1); 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci buf = number(buf, end, year, default_dec04_spec); 182562306a36Sopenharmony_ci if (buf < end) 182662306a36Sopenharmony_ci *buf = '-'; 182762306a36Sopenharmony_ci buf++; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci buf = number(buf, end, mon, default_dec02_spec); 183062306a36Sopenharmony_ci if (buf < end) 183162306a36Sopenharmony_ci *buf = '-'; 183262306a36Sopenharmony_ci buf++; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci return number(buf, end, tm->tm_mday, default_dec02_spec); 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_cistatic noinline_for_stack 183862306a36Sopenharmony_cichar *time_str(char *buf, char *end, const struct rtc_time *tm, bool r) 183962306a36Sopenharmony_ci{ 184062306a36Sopenharmony_ci buf = number(buf, end, tm->tm_hour, default_dec02_spec); 184162306a36Sopenharmony_ci if (buf < end) 184262306a36Sopenharmony_ci *buf = ':'; 184362306a36Sopenharmony_ci buf++; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci buf = number(buf, end, tm->tm_min, default_dec02_spec); 184662306a36Sopenharmony_ci if (buf < end) 184762306a36Sopenharmony_ci *buf = ':'; 184862306a36Sopenharmony_ci buf++; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci return number(buf, end, tm->tm_sec, default_dec02_spec); 185162306a36Sopenharmony_ci} 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_cistatic noinline_for_stack 185462306a36Sopenharmony_cichar *rtc_str(char *buf, char *end, const struct rtc_time *tm, 185562306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 185662306a36Sopenharmony_ci{ 185762306a36Sopenharmony_ci bool have_t = true, have_d = true; 185862306a36Sopenharmony_ci bool raw = false, iso8601_separator = true; 185962306a36Sopenharmony_ci bool found = true; 186062306a36Sopenharmony_ci int count = 2; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci if (check_pointer(&buf, end, tm, spec)) 186362306a36Sopenharmony_ci return buf; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci switch (fmt[count]) { 186662306a36Sopenharmony_ci case 'd': 186762306a36Sopenharmony_ci have_t = false; 186862306a36Sopenharmony_ci count++; 186962306a36Sopenharmony_ci break; 187062306a36Sopenharmony_ci case 't': 187162306a36Sopenharmony_ci have_d = false; 187262306a36Sopenharmony_ci count++; 187362306a36Sopenharmony_ci break; 187462306a36Sopenharmony_ci } 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci do { 187762306a36Sopenharmony_ci switch (fmt[count++]) { 187862306a36Sopenharmony_ci case 'r': 187962306a36Sopenharmony_ci raw = true; 188062306a36Sopenharmony_ci break; 188162306a36Sopenharmony_ci case 's': 188262306a36Sopenharmony_ci iso8601_separator = false; 188362306a36Sopenharmony_ci break; 188462306a36Sopenharmony_ci default: 188562306a36Sopenharmony_ci found = false; 188662306a36Sopenharmony_ci break; 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci } while (found); 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci if (have_d) 189162306a36Sopenharmony_ci buf = date_str(buf, end, tm, raw); 189262306a36Sopenharmony_ci if (have_d && have_t) { 189362306a36Sopenharmony_ci if (buf < end) 189462306a36Sopenharmony_ci *buf = iso8601_separator ? 'T' : ' '; 189562306a36Sopenharmony_ci buf++; 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci if (have_t) 189862306a36Sopenharmony_ci buf = time_str(buf, end, tm, raw); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci return buf; 190162306a36Sopenharmony_ci} 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_cistatic noinline_for_stack 190462306a36Sopenharmony_cichar *time64_str(char *buf, char *end, const time64_t time, 190562306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 190662306a36Sopenharmony_ci{ 190762306a36Sopenharmony_ci struct rtc_time rtc_time; 190862306a36Sopenharmony_ci struct tm tm; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci time64_to_tm(time, 0, &tm); 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci rtc_time.tm_sec = tm.tm_sec; 191362306a36Sopenharmony_ci rtc_time.tm_min = tm.tm_min; 191462306a36Sopenharmony_ci rtc_time.tm_hour = tm.tm_hour; 191562306a36Sopenharmony_ci rtc_time.tm_mday = tm.tm_mday; 191662306a36Sopenharmony_ci rtc_time.tm_mon = tm.tm_mon; 191762306a36Sopenharmony_ci rtc_time.tm_year = tm.tm_year; 191862306a36Sopenharmony_ci rtc_time.tm_wday = tm.tm_wday; 191962306a36Sopenharmony_ci rtc_time.tm_yday = tm.tm_yday; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci rtc_time.tm_isdst = 0; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci return rtc_str(buf, end, &rtc_time, spec, fmt); 192462306a36Sopenharmony_ci} 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_cistatic noinline_for_stack 192762306a36Sopenharmony_cichar *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec, 192862306a36Sopenharmony_ci const char *fmt) 192962306a36Sopenharmony_ci{ 193062306a36Sopenharmony_ci switch (fmt[1]) { 193162306a36Sopenharmony_ci case 'R': 193262306a36Sopenharmony_ci return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt); 193362306a36Sopenharmony_ci case 'T': 193462306a36Sopenharmony_ci return time64_str(buf, end, *(const time64_t *)ptr, spec, fmt); 193562306a36Sopenharmony_ci default: 193662306a36Sopenharmony_ci return error_string(buf, end, "(%pt?)", spec); 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci} 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_cistatic noinline_for_stack 194162306a36Sopenharmony_cichar *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec, 194262306a36Sopenharmony_ci const char *fmt) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_HAVE_CLK)) 194562306a36Sopenharmony_ci return error_string(buf, end, "(%pC?)", spec); 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci if (check_pointer(&buf, end, clk, spec)) 194862306a36Sopenharmony_ci return buf; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci switch (fmt[1]) { 195162306a36Sopenharmony_ci case 'n': 195262306a36Sopenharmony_ci default: 195362306a36Sopenharmony_ci#ifdef CONFIG_COMMON_CLK 195462306a36Sopenharmony_ci return string(buf, end, __clk_get_name(clk), spec); 195562306a36Sopenharmony_ci#else 195662306a36Sopenharmony_ci return ptr_to_id(buf, end, clk, spec); 195762306a36Sopenharmony_ci#endif 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_cistatic 196262306a36Sopenharmony_cichar *format_flags(char *buf, char *end, unsigned long flags, 196362306a36Sopenharmony_ci const struct trace_print_flags *names) 196462306a36Sopenharmony_ci{ 196562306a36Sopenharmony_ci unsigned long mask; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci for ( ; flags && names->name; names++) { 196862306a36Sopenharmony_ci mask = names->mask; 196962306a36Sopenharmony_ci if ((flags & mask) != mask) 197062306a36Sopenharmony_ci continue; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci buf = string(buf, end, names->name, default_str_spec); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci flags &= ~mask; 197562306a36Sopenharmony_ci if (flags) { 197662306a36Sopenharmony_ci if (buf < end) 197762306a36Sopenharmony_ci *buf = '|'; 197862306a36Sopenharmony_ci buf++; 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci if (flags) 198362306a36Sopenharmony_ci buf = number(buf, end, flags, default_flag_spec); 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci return buf; 198662306a36Sopenharmony_ci} 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_cistruct page_flags_fields { 198962306a36Sopenharmony_ci int width; 199062306a36Sopenharmony_ci int shift; 199162306a36Sopenharmony_ci int mask; 199262306a36Sopenharmony_ci const struct printf_spec *spec; 199362306a36Sopenharmony_ci const char *name; 199462306a36Sopenharmony_ci}; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_cistatic const struct page_flags_fields pff[] = { 199762306a36Sopenharmony_ci {SECTIONS_WIDTH, SECTIONS_PGSHIFT, SECTIONS_MASK, 199862306a36Sopenharmony_ci &default_dec_spec, "section"}, 199962306a36Sopenharmony_ci {NODES_WIDTH, NODES_PGSHIFT, NODES_MASK, 200062306a36Sopenharmony_ci &default_dec_spec, "node"}, 200162306a36Sopenharmony_ci {ZONES_WIDTH, ZONES_PGSHIFT, ZONES_MASK, 200262306a36Sopenharmony_ci &default_dec_spec, "zone"}, 200362306a36Sopenharmony_ci {LAST_CPUPID_WIDTH, LAST_CPUPID_PGSHIFT, LAST_CPUPID_MASK, 200462306a36Sopenharmony_ci &default_flag_spec, "lastcpupid"}, 200562306a36Sopenharmony_ci {KASAN_TAG_WIDTH, KASAN_TAG_PGSHIFT, KASAN_TAG_MASK, 200662306a36Sopenharmony_ci &default_flag_spec, "kasantag"}, 200762306a36Sopenharmony_ci}; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_cistatic 201062306a36Sopenharmony_cichar *format_page_flags(char *buf, char *end, unsigned long flags) 201162306a36Sopenharmony_ci{ 201262306a36Sopenharmony_ci unsigned long main_flags = flags & PAGEFLAGS_MASK; 201362306a36Sopenharmony_ci bool append = false; 201462306a36Sopenharmony_ci int i; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci buf = number(buf, end, flags, default_flag_spec); 201762306a36Sopenharmony_ci if (buf < end) 201862306a36Sopenharmony_ci *buf = '('; 201962306a36Sopenharmony_ci buf++; 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci /* Page flags from the main area. */ 202262306a36Sopenharmony_ci if (main_flags) { 202362306a36Sopenharmony_ci buf = format_flags(buf, end, main_flags, pageflag_names); 202462306a36Sopenharmony_ci append = true; 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci /* Page flags from the fields area */ 202862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pff); i++) { 202962306a36Sopenharmony_ci /* Skip undefined fields. */ 203062306a36Sopenharmony_ci if (!pff[i].width) 203162306a36Sopenharmony_ci continue; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci /* Format: Flag Name + '=' (equals sign) + Number + '|' (separator) */ 203462306a36Sopenharmony_ci if (append) { 203562306a36Sopenharmony_ci if (buf < end) 203662306a36Sopenharmony_ci *buf = '|'; 203762306a36Sopenharmony_ci buf++; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci buf = string(buf, end, pff[i].name, default_str_spec); 204162306a36Sopenharmony_ci if (buf < end) 204262306a36Sopenharmony_ci *buf = '='; 204362306a36Sopenharmony_ci buf++; 204462306a36Sopenharmony_ci buf = number(buf, end, (flags >> pff[i].shift) & pff[i].mask, 204562306a36Sopenharmony_ci *pff[i].spec); 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci append = true; 204862306a36Sopenharmony_ci } 204962306a36Sopenharmony_ci if (buf < end) 205062306a36Sopenharmony_ci *buf = ')'; 205162306a36Sopenharmony_ci buf++; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci return buf; 205462306a36Sopenharmony_ci} 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_cistatic 205762306a36Sopenharmony_cichar *format_page_type(char *buf, char *end, unsigned int page_type) 205862306a36Sopenharmony_ci{ 205962306a36Sopenharmony_ci buf = number(buf, end, page_type, default_flag_spec); 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci if (buf < end) 206262306a36Sopenharmony_ci *buf = '('; 206362306a36Sopenharmony_ci buf++; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci if (page_type_has_type(page_type)) 206662306a36Sopenharmony_ci buf = format_flags(buf, end, ~page_type, pagetype_names); 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci if (buf < end) 206962306a36Sopenharmony_ci *buf = ')'; 207062306a36Sopenharmony_ci buf++; 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci return buf; 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_cistatic noinline_for_stack 207662306a36Sopenharmony_cichar *flags_string(char *buf, char *end, void *flags_ptr, 207762306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 207862306a36Sopenharmony_ci{ 207962306a36Sopenharmony_ci unsigned long flags; 208062306a36Sopenharmony_ci const struct trace_print_flags *names; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci if (check_pointer(&buf, end, flags_ptr, spec)) 208362306a36Sopenharmony_ci return buf; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci switch (fmt[1]) { 208662306a36Sopenharmony_ci case 'p': 208762306a36Sopenharmony_ci return format_page_flags(buf, end, *(unsigned long *)flags_ptr); 208862306a36Sopenharmony_ci case 't': 208962306a36Sopenharmony_ci return format_page_type(buf, end, *(unsigned int *)flags_ptr); 209062306a36Sopenharmony_ci case 'v': 209162306a36Sopenharmony_ci flags = *(unsigned long *)flags_ptr; 209262306a36Sopenharmony_ci names = vmaflag_names; 209362306a36Sopenharmony_ci break; 209462306a36Sopenharmony_ci case 'g': 209562306a36Sopenharmony_ci flags = (__force unsigned long)(*(gfp_t *)flags_ptr); 209662306a36Sopenharmony_ci names = gfpflag_names; 209762306a36Sopenharmony_ci break; 209862306a36Sopenharmony_ci default: 209962306a36Sopenharmony_ci return error_string(buf, end, "(%pG?)", spec); 210062306a36Sopenharmony_ci } 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci return format_flags(buf, end, flags, names); 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic noinline_for_stack 210662306a36Sopenharmony_cichar *fwnode_full_name_string(struct fwnode_handle *fwnode, char *buf, 210762306a36Sopenharmony_ci char *end) 210862306a36Sopenharmony_ci{ 210962306a36Sopenharmony_ci int depth; 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci /* Loop starting from the root node to the current node. */ 211262306a36Sopenharmony_ci for (depth = fwnode_count_parents(fwnode); depth >= 0; depth--) { 211362306a36Sopenharmony_ci /* 211462306a36Sopenharmony_ci * Only get a reference for other nodes (i.e. parent nodes). 211562306a36Sopenharmony_ci * fwnode refcount may be 0 here. 211662306a36Sopenharmony_ci */ 211762306a36Sopenharmony_ci struct fwnode_handle *__fwnode = depth ? 211862306a36Sopenharmony_ci fwnode_get_nth_parent(fwnode, depth) : fwnode; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci buf = string(buf, end, fwnode_get_name_prefix(__fwnode), 212162306a36Sopenharmony_ci default_str_spec); 212262306a36Sopenharmony_ci buf = string(buf, end, fwnode_get_name(__fwnode), 212362306a36Sopenharmony_ci default_str_spec); 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci if (depth) 212662306a36Sopenharmony_ci fwnode_handle_put(__fwnode); 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci return buf; 213062306a36Sopenharmony_ci} 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_cistatic noinline_for_stack 213362306a36Sopenharmony_cichar *device_node_string(char *buf, char *end, struct device_node *dn, 213462306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 213562306a36Sopenharmony_ci{ 213662306a36Sopenharmony_ci char tbuf[sizeof("xxxx") + 1]; 213762306a36Sopenharmony_ci const char *p; 213862306a36Sopenharmony_ci int ret; 213962306a36Sopenharmony_ci char *buf_start = buf; 214062306a36Sopenharmony_ci struct property *prop; 214162306a36Sopenharmony_ci bool has_mult, pass; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci struct printf_spec str_spec = spec; 214462306a36Sopenharmony_ci str_spec.field_width = -1; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (fmt[0] != 'F') 214762306a36Sopenharmony_ci return error_string(buf, end, "(%pO?)", spec); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_OF)) 215062306a36Sopenharmony_ci return error_string(buf, end, "(%pOF?)", spec); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci if (check_pointer(&buf, end, dn, spec)) 215362306a36Sopenharmony_ci return buf; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci /* simple case without anything any more format specifiers */ 215662306a36Sopenharmony_ci fmt++; 215762306a36Sopenharmony_ci if (fmt[0] == '\0' || strcspn(fmt,"fnpPFcC") > 0) 215862306a36Sopenharmony_ci fmt = "f"; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci for (pass = false; strspn(fmt,"fnpPFcC"); fmt++, pass = true) { 216162306a36Sopenharmony_ci int precision; 216262306a36Sopenharmony_ci if (pass) { 216362306a36Sopenharmony_ci if (buf < end) 216462306a36Sopenharmony_ci *buf = ':'; 216562306a36Sopenharmony_ci buf++; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci switch (*fmt) { 216962306a36Sopenharmony_ci case 'f': /* full_name */ 217062306a36Sopenharmony_ci buf = fwnode_full_name_string(of_fwnode_handle(dn), buf, 217162306a36Sopenharmony_ci end); 217262306a36Sopenharmony_ci break; 217362306a36Sopenharmony_ci case 'n': /* name */ 217462306a36Sopenharmony_ci p = fwnode_get_name(of_fwnode_handle(dn)); 217562306a36Sopenharmony_ci precision = str_spec.precision; 217662306a36Sopenharmony_ci str_spec.precision = strchrnul(p, '@') - p; 217762306a36Sopenharmony_ci buf = string(buf, end, p, str_spec); 217862306a36Sopenharmony_ci str_spec.precision = precision; 217962306a36Sopenharmony_ci break; 218062306a36Sopenharmony_ci case 'p': /* phandle */ 218162306a36Sopenharmony_ci buf = number(buf, end, (unsigned int)dn->phandle, default_dec_spec); 218262306a36Sopenharmony_ci break; 218362306a36Sopenharmony_ci case 'P': /* path-spec */ 218462306a36Sopenharmony_ci p = fwnode_get_name(of_fwnode_handle(dn)); 218562306a36Sopenharmony_ci if (!p[1]) 218662306a36Sopenharmony_ci p = "/"; 218762306a36Sopenharmony_ci buf = string(buf, end, p, str_spec); 218862306a36Sopenharmony_ci break; 218962306a36Sopenharmony_ci case 'F': /* flags */ 219062306a36Sopenharmony_ci tbuf[0] = of_node_check_flag(dn, OF_DYNAMIC) ? 'D' : '-'; 219162306a36Sopenharmony_ci tbuf[1] = of_node_check_flag(dn, OF_DETACHED) ? 'd' : '-'; 219262306a36Sopenharmony_ci tbuf[2] = of_node_check_flag(dn, OF_POPULATED) ? 'P' : '-'; 219362306a36Sopenharmony_ci tbuf[3] = of_node_check_flag(dn, OF_POPULATED_BUS) ? 'B' : '-'; 219462306a36Sopenharmony_ci tbuf[4] = 0; 219562306a36Sopenharmony_ci buf = string_nocheck(buf, end, tbuf, str_spec); 219662306a36Sopenharmony_ci break; 219762306a36Sopenharmony_ci case 'c': /* major compatible string */ 219862306a36Sopenharmony_ci ret = of_property_read_string(dn, "compatible", &p); 219962306a36Sopenharmony_ci if (!ret) 220062306a36Sopenharmony_ci buf = string(buf, end, p, str_spec); 220162306a36Sopenharmony_ci break; 220262306a36Sopenharmony_ci case 'C': /* full compatible string */ 220362306a36Sopenharmony_ci has_mult = false; 220462306a36Sopenharmony_ci of_property_for_each_string(dn, "compatible", prop, p) { 220562306a36Sopenharmony_ci if (has_mult) 220662306a36Sopenharmony_ci buf = string_nocheck(buf, end, ",", str_spec); 220762306a36Sopenharmony_ci buf = string_nocheck(buf, end, "\"", str_spec); 220862306a36Sopenharmony_ci buf = string(buf, end, p, str_spec); 220962306a36Sopenharmony_ci buf = string_nocheck(buf, end, "\"", str_spec); 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci has_mult = true; 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci break; 221462306a36Sopenharmony_ci default: 221562306a36Sopenharmony_ci break; 221662306a36Sopenharmony_ci } 221762306a36Sopenharmony_ci } 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci return widen_string(buf, buf - buf_start, end, spec); 222062306a36Sopenharmony_ci} 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_cistatic noinline_for_stack 222362306a36Sopenharmony_cichar *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode, 222462306a36Sopenharmony_ci struct printf_spec spec, const char *fmt) 222562306a36Sopenharmony_ci{ 222662306a36Sopenharmony_ci struct printf_spec str_spec = spec; 222762306a36Sopenharmony_ci char *buf_start = buf; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci str_spec.field_width = -1; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci if (*fmt != 'w') 223262306a36Sopenharmony_ci return error_string(buf, end, "(%pf?)", spec); 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci if (check_pointer(&buf, end, fwnode, spec)) 223562306a36Sopenharmony_ci return buf; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci fmt++; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci switch (*fmt) { 224062306a36Sopenharmony_ci case 'P': /* name */ 224162306a36Sopenharmony_ci buf = string(buf, end, fwnode_get_name(fwnode), str_spec); 224262306a36Sopenharmony_ci break; 224362306a36Sopenharmony_ci case 'f': /* full_name */ 224462306a36Sopenharmony_ci default: 224562306a36Sopenharmony_ci buf = fwnode_full_name_string(fwnode, buf, end); 224662306a36Sopenharmony_ci break; 224762306a36Sopenharmony_ci } 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci return widen_string(buf, buf - buf_start, end, spec); 225062306a36Sopenharmony_ci} 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ciint __init no_hash_pointers_enable(char *str) 225362306a36Sopenharmony_ci{ 225462306a36Sopenharmony_ci if (no_hash_pointers) 225562306a36Sopenharmony_ci return 0; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci no_hash_pointers = true; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci pr_warn("**********************************************************\n"); 226062306a36Sopenharmony_ci pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); 226162306a36Sopenharmony_ci pr_warn("** **\n"); 226262306a36Sopenharmony_ci pr_warn("** This system shows unhashed kernel memory addresses **\n"); 226362306a36Sopenharmony_ci pr_warn("** via the console, logs, and other interfaces. This **\n"); 226462306a36Sopenharmony_ci pr_warn("** might reduce the security of your system. **\n"); 226562306a36Sopenharmony_ci pr_warn("** **\n"); 226662306a36Sopenharmony_ci pr_warn("** If you see this message and you are not debugging **\n"); 226762306a36Sopenharmony_ci pr_warn("** the kernel, report this immediately to your system **\n"); 226862306a36Sopenharmony_ci pr_warn("** administrator! **\n"); 226962306a36Sopenharmony_ci pr_warn("** **\n"); 227062306a36Sopenharmony_ci pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); 227162306a36Sopenharmony_ci pr_warn("**********************************************************\n"); 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci return 0; 227462306a36Sopenharmony_ci} 227562306a36Sopenharmony_ciearly_param("no_hash_pointers", no_hash_pointers_enable); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci/* Used for Rust formatting ('%pA'). */ 227862306a36Sopenharmony_cichar *rust_fmt_argument(char *buf, char *end, void *ptr); 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci/* 228162306a36Sopenharmony_ci * Show a '%p' thing. A kernel extension is that the '%p' is followed 228262306a36Sopenharmony_ci * by an extra set of alphanumeric characters that are extended format 228362306a36Sopenharmony_ci * specifiers. 228462306a36Sopenharmony_ci * 228562306a36Sopenharmony_ci * Please update scripts/checkpatch.pl when adding/removing conversion 228662306a36Sopenharmony_ci * characters. (Search for "check for vsprintf extension"). 228762306a36Sopenharmony_ci * 228862306a36Sopenharmony_ci * Right now we handle: 228962306a36Sopenharmony_ci * 229062306a36Sopenharmony_ci * - 'S' For symbolic direct pointers (or function descriptors) with offset 229162306a36Sopenharmony_ci * - 's' For symbolic direct pointers (or function descriptors) without offset 229262306a36Sopenharmony_ci * - '[Ss]R' as above with __builtin_extract_return_addr() translation 229362306a36Sopenharmony_ci * - 'S[R]b' as above with module build ID (for use in backtraces) 229462306a36Sopenharmony_ci * - '[Ff]' %pf and %pF were obsoleted and later removed in favor of 229562306a36Sopenharmony_ci * %ps and %pS. Be careful when re-using these specifiers. 229662306a36Sopenharmony_ci * - 'B' For backtraced symbolic direct pointers with offset 229762306a36Sopenharmony_ci * - 'Bb' as above with module build ID (for use in backtraces) 229862306a36Sopenharmony_ci * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref] 229962306a36Sopenharmony_ci * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201] 230062306a36Sopenharmony_ci * - 'b[l]' For a bitmap, the number of bits is determined by the field 230162306a36Sopenharmony_ci * width which must be explicitly specified either as part of the 230262306a36Sopenharmony_ci * format string '%32b[l]' or through '%*b[l]', [l] selects 230362306a36Sopenharmony_ci * range-list format instead of hex format 230462306a36Sopenharmony_ci * - 'M' For a 6-byte MAC address, it prints the address in the 230562306a36Sopenharmony_ci * usual colon-separated hex notation 230662306a36Sopenharmony_ci * - 'm' For a 6-byte MAC address, it prints the hex address without colons 230762306a36Sopenharmony_ci * - 'MF' For a 6-byte MAC FDDI address, it prints the address 230862306a36Sopenharmony_ci * with a dash-separated hex notation 230962306a36Sopenharmony_ci * - '[mM]R' For a 6-byte MAC address, Reverse order (Bluetooth) 231062306a36Sopenharmony_ci * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way 231162306a36Sopenharmony_ci * IPv4 uses dot-separated decimal without leading 0's (1.2.3.4) 231262306a36Sopenharmony_ci * IPv6 uses colon separated network-order 16 bit hex with leading 0's 231362306a36Sopenharmony_ci * [S][pfs] 231462306a36Sopenharmony_ci * Generic IPv4/IPv6 address (struct sockaddr *) that falls back to 231562306a36Sopenharmony_ci * [4] or [6] and is able to print port [p], flowinfo [f], scope [s] 231662306a36Sopenharmony_ci * - 'i' [46] for 'raw' IPv4/IPv6 addresses 231762306a36Sopenharmony_ci * IPv6 omits the colons (01020304...0f) 231862306a36Sopenharmony_ci * IPv4 uses dot-separated decimal with leading 0's (010.123.045.006) 231962306a36Sopenharmony_ci * [S][pfs] 232062306a36Sopenharmony_ci * Generic IPv4/IPv6 address (struct sockaddr *) that falls back to 232162306a36Sopenharmony_ci * [4] or [6] and is able to print port [p], flowinfo [f], scope [s] 232262306a36Sopenharmony_ci * - '[Ii][4S][hnbl]' IPv4 addresses in host, network, big or little endian order 232362306a36Sopenharmony_ci * - 'I[6S]c' for IPv6 addresses printed as specified by 232462306a36Sopenharmony_ci * https://tools.ietf.org/html/rfc5952 232562306a36Sopenharmony_ci * - 'E[achnops]' For an escaped buffer, where rules are defined by combination 232662306a36Sopenharmony_ci * of the following flags (see string_escape_mem() for the 232762306a36Sopenharmony_ci * details): 232862306a36Sopenharmony_ci * a - ESCAPE_ANY 232962306a36Sopenharmony_ci * c - ESCAPE_SPECIAL 233062306a36Sopenharmony_ci * h - ESCAPE_HEX 233162306a36Sopenharmony_ci * n - ESCAPE_NULL 233262306a36Sopenharmony_ci * o - ESCAPE_OCTAL 233362306a36Sopenharmony_ci * p - ESCAPE_NP 233462306a36Sopenharmony_ci * s - ESCAPE_SPACE 233562306a36Sopenharmony_ci * By default ESCAPE_ANY_NP is used. 233662306a36Sopenharmony_ci * - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form 233762306a36Sopenharmony_ci * "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 233862306a36Sopenharmony_ci * Options for %pU are: 233962306a36Sopenharmony_ci * b big endian lower case hex (default) 234062306a36Sopenharmony_ci * B big endian UPPER case hex 234162306a36Sopenharmony_ci * l little endian lower case hex 234262306a36Sopenharmony_ci * L little endian UPPER case hex 234362306a36Sopenharmony_ci * big endian output byte order is: 234462306a36Sopenharmony_ci * [0][1][2][3]-[4][5]-[6][7]-[8][9]-[10][11][12][13][14][15] 234562306a36Sopenharmony_ci * little endian output byte order is: 234662306a36Sopenharmony_ci * [3][2][1][0]-[5][4]-[7][6]-[8][9]-[10][11][12][13][14][15] 234762306a36Sopenharmony_ci * - 'V' For a struct va_format which contains a format string * and va_list *, 234862306a36Sopenharmony_ci * call vsnprintf(->format, *->va_list). 234962306a36Sopenharmony_ci * Implements a "recursive vsnprintf". 235062306a36Sopenharmony_ci * Do not use this feature without some mechanism to verify the 235162306a36Sopenharmony_ci * correctness of the format string and va_list arguments. 235262306a36Sopenharmony_ci * - 'K' For a kernel pointer that should be hidden from unprivileged users. 235362306a36Sopenharmony_ci * Use only for procfs, sysfs and similar files, not printk(); please 235462306a36Sopenharmony_ci * read the documentation (path below) first. 235562306a36Sopenharmony_ci * - 'NF' For a netdev_features_t 235662306a36Sopenharmony_ci * - '4cc' V4L2 or DRM FourCC code, with endianness and raw numerical value. 235762306a36Sopenharmony_ci * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with 235862306a36Sopenharmony_ci * a certain separator (' ' by default): 235962306a36Sopenharmony_ci * C colon 236062306a36Sopenharmony_ci * D dash 236162306a36Sopenharmony_ci * N no separator 236262306a36Sopenharmony_ci * The maximum supported length is 64 bytes of the input. Consider 236362306a36Sopenharmony_ci * to use print_hex_dump() for the larger input. 236462306a36Sopenharmony_ci * - 'a[pd]' For address types [p] phys_addr_t, [d] dma_addr_t and derivatives 236562306a36Sopenharmony_ci * (default assumed to be phys_addr_t, passed by reference) 236662306a36Sopenharmony_ci * - 'd[234]' For a dentry name (optionally 2-4 last components) 236762306a36Sopenharmony_ci * - 'D[234]' Same as 'd' but for a struct file 236862306a36Sopenharmony_ci * - 'g' For block_device name (gendisk + partition number) 236962306a36Sopenharmony_ci * - 't[RT][dt][r][s]' For time and date as represented by: 237062306a36Sopenharmony_ci * R struct rtc_time 237162306a36Sopenharmony_ci * T time64_t 237262306a36Sopenharmony_ci * - 'C' For a clock, it prints the name (Common Clock Framework) or address 237362306a36Sopenharmony_ci * (legacy clock framework) of the clock 237462306a36Sopenharmony_ci * - 'Cn' For a clock, it prints the name (Common Clock Framework) or address 237562306a36Sopenharmony_ci * (legacy clock framework) of the clock 237662306a36Sopenharmony_ci * - 'G' For flags to be printed as a collection of symbolic strings that would 237762306a36Sopenharmony_ci * construct the specific value. Supported flags given by option: 237862306a36Sopenharmony_ci * p page flags (see struct page) given as pointer to unsigned long 237962306a36Sopenharmony_ci * g gfp flags (GFP_* and __GFP_*) given as pointer to gfp_t 238062306a36Sopenharmony_ci * v vma flags (VM_*) given as pointer to unsigned long 238162306a36Sopenharmony_ci * - 'OF[fnpPcCF]' For a device tree object 238262306a36Sopenharmony_ci * Without any optional arguments prints the full_name 238362306a36Sopenharmony_ci * f device node full_name 238462306a36Sopenharmony_ci * n device node name 238562306a36Sopenharmony_ci * p device node phandle 238662306a36Sopenharmony_ci * P device node path spec (name + @unit) 238762306a36Sopenharmony_ci * F device node flags 238862306a36Sopenharmony_ci * c major compatible string 238962306a36Sopenharmony_ci * C full compatible string 239062306a36Sopenharmony_ci * - 'fw[fP]' For a firmware node (struct fwnode_handle) pointer 239162306a36Sopenharmony_ci * Without an option prints the full name of the node 239262306a36Sopenharmony_ci * f full name 239362306a36Sopenharmony_ci * P node name, including a possible unit address 239462306a36Sopenharmony_ci * - 'x' For printing the address unmodified. Equivalent to "%lx". 239562306a36Sopenharmony_ci * Please read the documentation (path below) before using! 239662306a36Sopenharmony_ci * - '[ku]s' For a BPF/tracing related format specifier, e.g. used out of 239762306a36Sopenharmony_ci * bpf_trace_printk() where [ku] prefix specifies either kernel (k) 239862306a36Sopenharmony_ci * or user (u) memory to probe, and: 239962306a36Sopenharmony_ci * s a string, equivalent to "%s" on direct vsnprintf() use 240062306a36Sopenharmony_ci * 240162306a36Sopenharmony_ci * ** When making changes please also update: 240262306a36Sopenharmony_ci * Documentation/core-api/printk-formats.rst 240362306a36Sopenharmony_ci * 240462306a36Sopenharmony_ci * Note: The default behaviour (unadorned %p) is to hash the address, 240562306a36Sopenharmony_ci * rendering it useful as a unique identifier. 240662306a36Sopenharmony_ci * 240762306a36Sopenharmony_ci * There is also a '%pA' format specifier, but it is only intended to be used 240862306a36Sopenharmony_ci * from Rust code to format core::fmt::Arguments. Do *not* use it from C. 240962306a36Sopenharmony_ci * See rust/kernel/print.rs for details. 241062306a36Sopenharmony_ci */ 241162306a36Sopenharmony_cistatic noinline_for_stack 241262306a36Sopenharmony_cichar *pointer(const char *fmt, char *buf, char *end, void *ptr, 241362306a36Sopenharmony_ci struct printf_spec spec) 241462306a36Sopenharmony_ci{ 241562306a36Sopenharmony_ci switch (*fmt) { 241662306a36Sopenharmony_ci case 'S': 241762306a36Sopenharmony_ci case 's': 241862306a36Sopenharmony_ci ptr = dereference_symbol_descriptor(ptr); 241962306a36Sopenharmony_ci fallthrough; 242062306a36Sopenharmony_ci case 'B': 242162306a36Sopenharmony_ci return symbol_string(buf, end, ptr, spec, fmt); 242262306a36Sopenharmony_ci case 'R': 242362306a36Sopenharmony_ci case 'r': 242462306a36Sopenharmony_ci return resource_string(buf, end, ptr, spec, fmt); 242562306a36Sopenharmony_ci case 'h': 242662306a36Sopenharmony_ci return hex_string(buf, end, ptr, spec, fmt); 242762306a36Sopenharmony_ci case 'b': 242862306a36Sopenharmony_ci switch (fmt[1]) { 242962306a36Sopenharmony_ci case 'l': 243062306a36Sopenharmony_ci return bitmap_list_string(buf, end, ptr, spec, fmt); 243162306a36Sopenharmony_ci default: 243262306a36Sopenharmony_ci return bitmap_string(buf, end, ptr, spec, fmt); 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci case 'M': /* Colon separated: 00:01:02:03:04:05 */ 243562306a36Sopenharmony_ci case 'm': /* Contiguous: 000102030405 */ 243662306a36Sopenharmony_ci /* [mM]F (FDDI) */ 243762306a36Sopenharmony_ci /* [mM]R (Reverse order; Bluetooth) */ 243862306a36Sopenharmony_ci return mac_address_string(buf, end, ptr, spec, fmt); 243962306a36Sopenharmony_ci case 'I': /* Formatted IP supported 244062306a36Sopenharmony_ci * 4: 1.2.3.4 244162306a36Sopenharmony_ci * 6: 0001:0203:...:0708 244262306a36Sopenharmony_ci * 6c: 1::708 or 1::1.2.3.4 244362306a36Sopenharmony_ci */ 244462306a36Sopenharmony_ci case 'i': /* Contiguous: 244562306a36Sopenharmony_ci * 4: 001.002.003.004 244662306a36Sopenharmony_ci * 6: 000102...0f 244762306a36Sopenharmony_ci */ 244862306a36Sopenharmony_ci return ip_addr_string(buf, end, ptr, spec, fmt); 244962306a36Sopenharmony_ci case 'E': 245062306a36Sopenharmony_ci return escaped_string(buf, end, ptr, spec, fmt); 245162306a36Sopenharmony_ci case 'U': 245262306a36Sopenharmony_ci return uuid_string(buf, end, ptr, spec, fmt); 245362306a36Sopenharmony_ci case 'V': 245462306a36Sopenharmony_ci return va_format(buf, end, ptr, spec, fmt); 245562306a36Sopenharmony_ci case 'K': 245662306a36Sopenharmony_ci return restricted_pointer(buf, end, ptr, spec); 245762306a36Sopenharmony_ci case 'N': 245862306a36Sopenharmony_ci return netdev_bits(buf, end, ptr, spec, fmt); 245962306a36Sopenharmony_ci case '4': 246062306a36Sopenharmony_ci return fourcc_string(buf, end, ptr, spec, fmt); 246162306a36Sopenharmony_ci case 'a': 246262306a36Sopenharmony_ci return address_val(buf, end, ptr, spec, fmt); 246362306a36Sopenharmony_ci case 'd': 246462306a36Sopenharmony_ci return dentry_name(buf, end, ptr, spec, fmt); 246562306a36Sopenharmony_ci case 't': 246662306a36Sopenharmony_ci return time_and_date(buf, end, ptr, spec, fmt); 246762306a36Sopenharmony_ci case 'C': 246862306a36Sopenharmony_ci return clock(buf, end, ptr, spec, fmt); 246962306a36Sopenharmony_ci case 'D': 247062306a36Sopenharmony_ci return file_dentry_name(buf, end, ptr, spec, fmt); 247162306a36Sopenharmony_ci#ifdef CONFIG_BLOCK 247262306a36Sopenharmony_ci case 'g': 247362306a36Sopenharmony_ci return bdev_name(buf, end, ptr, spec, fmt); 247462306a36Sopenharmony_ci#endif 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci case 'G': 247762306a36Sopenharmony_ci return flags_string(buf, end, ptr, spec, fmt); 247862306a36Sopenharmony_ci case 'O': 247962306a36Sopenharmony_ci return device_node_string(buf, end, ptr, spec, fmt + 1); 248062306a36Sopenharmony_ci case 'f': 248162306a36Sopenharmony_ci return fwnode_string(buf, end, ptr, spec, fmt + 1); 248262306a36Sopenharmony_ci case 'A': 248362306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_RUST)) { 248462306a36Sopenharmony_ci WARN_ONCE(1, "Please remove %%pA from non-Rust code\n"); 248562306a36Sopenharmony_ci return error_string(buf, end, "(%pA?)", spec); 248662306a36Sopenharmony_ci } 248762306a36Sopenharmony_ci return rust_fmt_argument(buf, end, ptr); 248862306a36Sopenharmony_ci case 'x': 248962306a36Sopenharmony_ci return pointer_string(buf, end, ptr, spec); 249062306a36Sopenharmony_ci case 'e': 249162306a36Sopenharmony_ci /* %pe with a non-ERR_PTR gets treated as plain %p */ 249262306a36Sopenharmony_ci if (!IS_ERR(ptr)) 249362306a36Sopenharmony_ci return default_pointer(buf, end, ptr, spec); 249462306a36Sopenharmony_ci return err_ptr(buf, end, ptr, spec); 249562306a36Sopenharmony_ci case 'u': 249662306a36Sopenharmony_ci case 'k': 249762306a36Sopenharmony_ci switch (fmt[1]) { 249862306a36Sopenharmony_ci case 's': 249962306a36Sopenharmony_ci return string(buf, end, ptr, spec); 250062306a36Sopenharmony_ci default: 250162306a36Sopenharmony_ci return error_string(buf, end, "(einval)", spec); 250262306a36Sopenharmony_ci } 250362306a36Sopenharmony_ci default: 250462306a36Sopenharmony_ci return default_pointer(buf, end, ptr, spec); 250562306a36Sopenharmony_ci } 250662306a36Sopenharmony_ci} 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci/* 250962306a36Sopenharmony_ci * Helper function to decode printf style format. 251062306a36Sopenharmony_ci * Each call decode a token from the format and return the 251162306a36Sopenharmony_ci * number of characters read (or likely the delta where it wants 251262306a36Sopenharmony_ci * to go on the next call). 251362306a36Sopenharmony_ci * The decoded token is returned through the parameters 251462306a36Sopenharmony_ci * 251562306a36Sopenharmony_ci * 'h', 'l', or 'L' for integer fields 251662306a36Sopenharmony_ci * 'z' support added 23/7/1999 S.H. 251762306a36Sopenharmony_ci * 'z' changed to 'Z' --davidm 1/25/99 251862306a36Sopenharmony_ci * 'Z' changed to 'z' --adobriyan 2017-01-25 251962306a36Sopenharmony_ci * 't' added for ptrdiff_t 252062306a36Sopenharmony_ci * 252162306a36Sopenharmony_ci * @fmt: the format string 252262306a36Sopenharmony_ci * @type of the token returned 252362306a36Sopenharmony_ci * @flags: various flags such as +, -, # tokens.. 252462306a36Sopenharmony_ci * @field_width: overwritten width 252562306a36Sopenharmony_ci * @base: base of the number (octal, hex, ...) 252662306a36Sopenharmony_ci * @precision: precision of a number 252762306a36Sopenharmony_ci * @qualifier: qualifier of a number (long, size_t, ...) 252862306a36Sopenharmony_ci */ 252962306a36Sopenharmony_cistatic noinline_for_stack 253062306a36Sopenharmony_ciint format_decode(const char *fmt, struct printf_spec *spec) 253162306a36Sopenharmony_ci{ 253262306a36Sopenharmony_ci const char *start = fmt; 253362306a36Sopenharmony_ci char qualifier; 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci /* we finished early by reading the field width */ 253662306a36Sopenharmony_ci if (spec->type == FORMAT_TYPE_WIDTH) { 253762306a36Sopenharmony_ci if (spec->field_width < 0) { 253862306a36Sopenharmony_ci spec->field_width = -spec->field_width; 253962306a36Sopenharmony_ci spec->flags |= LEFT; 254062306a36Sopenharmony_ci } 254162306a36Sopenharmony_ci spec->type = FORMAT_TYPE_NONE; 254262306a36Sopenharmony_ci goto precision; 254362306a36Sopenharmony_ci } 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci /* we finished early by reading the precision */ 254662306a36Sopenharmony_ci if (spec->type == FORMAT_TYPE_PRECISION) { 254762306a36Sopenharmony_ci if (spec->precision < 0) 254862306a36Sopenharmony_ci spec->precision = 0; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci spec->type = FORMAT_TYPE_NONE; 255162306a36Sopenharmony_ci goto qualifier; 255262306a36Sopenharmony_ci } 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci /* By default */ 255562306a36Sopenharmony_ci spec->type = FORMAT_TYPE_NONE; 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci for (; *fmt ; ++fmt) { 255862306a36Sopenharmony_ci if (*fmt == '%') 255962306a36Sopenharmony_ci break; 256062306a36Sopenharmony_ci } 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci /* Return the current non-format string */ 256362306a36Sopenharmony_ci if (fmt != start || !*fmt) 256462306a36Sopenharmony_ci return fmt - start; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci /* Process flags */ 256762306a36Sopenharmony_ci spec->flags = 0; 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci while (1) { /* this also skips first '%' */ 257062306a36Sopenharmony_ci bool found = true; 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci ++fmt; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci switch (*fmt) { 257562306a36Sopenharmony_ci case '-': spec->flags |= LEFT; break; 257662306a36Sopenharmony_ci case '+': spec->flags |= PLUS; break; 257762306a36Sopenharmony_ci case ' ': spec->flags |= SPACE; break; 257862306a36Sopenharmony_ci case '#': spec->flags |= SPECIAL; break; 257962306a36Sopenharmony_ci case '0': spec->flags |= ZEROPAD; break; 258062306a36Sopenharmony_ci default: found = false; 258162306a36Sopenharmony_ci } 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci if (!found) 258462306a36Sopenharmony_ci break; 258562306a36Sopenharmony_ci } 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci /* get field width */ 258862306a36Sopenharmony_ci spec->field_width = -1; 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci if (isdigit(*fmt)) 259162306a36Sopenharmony_ci spec->field_width = skip_atoi(&fmt); 259262306a36Sopenharmony_ci else if (*fmt == '*') { 259362306a36Sopenharmony_ci /* it's the next argument */ 259462306a36Sopenharmony_ci spec->type = FORMAT_TYPE_WIDTH; 259562306a36Sopenharmony_ci return ++fmt - start; 259662306a36Sopenharmony_ci } 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ciprecision: 259962306a36Sopenharmony_ci /* get the precision */ 260062306a36Sopenharmony_ci spec->precision = -1; 260162306a36Sopenharmony_ci if (*fmt == '.') { 260262306a36Sopenharmony_ci ++fmt; 260362306a36Sopenharmony_ci if (isdigit(*fmt)) { 260462306a36Sopenharmony_ci spec->precision = skip_atoi(&fmt); 260562306a36Sopenharmony_ci if (spec->precision < 0) 260662306a36Sopenharmony_ci spec->precision = 0; 260762306a36Sopenharmony_ci } else if (*fmt == '*') { 260862306a36Sopenharmony_ci /* it's the next argument */ 260962306a36Sopenharmony_ci spec->type = FORMAT_TYPE_PRECISION; 261062306a36Sopenharmony_ci return ++fmt - start; 261162306a36Sopenharmony_ci } 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ciqualifier: 261562306a36Sopenharmony_ci /* get the conversion qualifier */ 261662306a36Sopenharmony_ci qualifier = 0; 261762306a36Sopenharmony_ci if (*fmt == 'h' || _tolower(*fmt) == 'l' || 261862306a36Sopenharmony_ci *fmt == 'z' || *fmt == 't') { 261962306a36Sopenharmony_ci qualifier = *fmt++; 262062306a36Sopenharmony_ci if (unlikely(qualifier == *fmt)) { 262162306a36Sopenharmony_ci if (qualifier == 'l') { 262262306a36Sopenharmony_ci qualifier = 'L'; 262362306a36Sopenharmony_ci ++fmt; 262462306a36Sopenharmony_ci } else if (qualifier == 'h') { 262562306a36Sopenharmony_ci qualifier = 'H'; 262662306a36Sopenharmony_ci ++fmt; 262762306a36Sopenharmony_ci } 262862306a36Sopenharmony_ci } 262962306a36Sopenharmony_ci } 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci /* default base */ 263262306a36Sopenharmony_ci spec->base = 10; 263362306a36Sopenharmony_ci switch (*fmt) { 263462306a36Sopenharmony_ci case 'c': 263562306a36Sopenharmony_ci spec->type = FORMAT_TYPE_CHAR; 263662306a36Sopenharmony_ci return ++fmt - start; 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci case 's': 263962306a36Sopenharmony_ci spec->type = FORMAT_TYPE_STR; 264062306a36Sopenharmony_ci return ++fmt - start; 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ci case 'p': 264362306a36Sopenharmony_ci spec->type = FORMAT_TYPE_PTR; 264462306a36Sopenharmony_ci return ++fmt - start; 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci case '%': 264762306a36Sopenharmony_ci spec->type = FORMAT_TYPE_PERCENT_CHAR; 264862306a36Sopenharmony_ci return ++fmt - start; 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci /* integer number formats - set up the flags and "break" */ 265162306a36Sopenharmony_ci case 'o': 265262306a36Sopenharmony_ci spec->base = 8; 265362306a36Sopenharmony_ci break; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci case 'x': 265662306a36Sopenharmony_ci spec->flags |= SMALL; 265762306a36Sopenharmony_ci fallthrough; 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci case 'X': 266062306a36Sopenharmony_ci spec->base = 16; 266162306a36Sopenharmony_ci break; 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci case 'd': 266462306a36Sopenharmony_ci case 'i': 266562306a36Sopenharmony_ci spec->flags |= SIGN; 266662306a36Sopenharmony_ci break; 266762306a36Sopenharmony_ci case 'u': 266862306a36Sopenharmony_ci break; 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci case 'n': 267162306a36Sopenharmony_ci /* 267262306a36Sopenharmony_ci * Since %n poses a greater security risk than 267362306a36Sopenharmony_ci * utility, treat it as any other invalid or 267462306a36Sopenharmony_ci * unsupported format specifier. 267562306a36Sopenharmony_ci */ 267662306a36Sopenharmony_ci fallthrough; 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci default: 267962306a36Sopenharmony_ci WARN_ONCE(1, "Please remove unsupported %%%c in format string\n", *fmt); 268062306a36Sopenharmony_ci spec->type = FORMAT_TYPE_INVALID; 268162306a36Sopenharmony_ci return fmt - start; 268262306a36Sopenharmony_ci } 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci if (qualifier == 'L') 268562306a36Sopenharmony_ci spec->type = FORMAT_TYPE_LONG_LONG; 268662306a36Sopenharmony_ci else if (qualifier == 'l') { 268762306a36Sopenharmony_ci BUILD_BUG_ON(FORMAT_TYPE_ULONG + SIGN != FORMAT_TYPE_LONG); 268862306a36Sopenharmony_ci spec->type = FORMAT_TYPE_ULONG + (spec->flags & SIGN); 268962306a36Sopenharmony_ci } else if (qualifier == 'z') { 269062306a36Sopenharmony_ci spec->type = FORMAT_TYPE_SIZE_T; 269162306a36Sopenharmony_ci } else if (qualifier == 't') { 269262306a36Sopenharmony_ci spec->type = FORMAT_TYPE_PTRDIFF; 269362306a36Sopenharmony_ci } else if (qualifier == 'H') { 269462306a36Sopenharmony_ci BUILD_BUG_ON(FORMAT_TYPE_UBYTE + SIGN != FORMAT_TYPE_BYTE); 269562306a36Sopenharmony_ci spec->type = FORMAT_TYPE_UBYTE + (spec->flags & SIGN); 269662306a36Sopenharmony_ci } else if (qualifier == 'h') { 269762306a36Sopenharmony_ci BUILD_BUG_ON(FORMAT_TYPE_USHORT + SIGN != FORMAT_TYPE_SHORT); 269862306a36Sopenharmony_ci spec->type = FORMAT_TYPE_USHORT + (spec->flags & SIGN); 269962306a36Sopenharmony_ci } else { 270062306a36Sopenharmony_ci BUILD_BUG_ON(FORMAT_TYPE_UINT + SIGN != FORMAT_TYPE_INT); 270162306a36Sopenharmony_ci spec->type = FORMAT_TYPE_UINT + (spec->flags & SIGN); 270262306a36Sopenharmony_ci } 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci return ++fmt - start; 270562306a36Sopenharmony_ci} 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_cistatic void 270862306a36Sopenharmony_ciset_field_width(struct printf_spec *spec, int width) 270962306a36Sopenharmony_ci{ 271062306a36Sopenharmony_ci spec->field_width = width; 271162306a36Sopenharmony_ci if (WARN_ONCE(spec->field_width != width, "field width %d too large", width)) { 271262306a36Sopenharmony_ci spec->field_width = clamp(width, -FIELD_WIDTH_MAX, FIELD_WIDTH_MAX); 271362306a36Sopenharmony_ci } 271462306a36Sopenharmony_ci} 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_cistatic void 271762306a36Sopenharmony_ciset_precision(struct printf_spec *spec, int prec) 271862306a36Sopenharmony_ci{ 271962306a36Sopenharmony_ci spec->precision = prec; 272062306a36Sopenharmony_ci if (WARN_ONCE(spec->precision != prec, "precision %d too large", prec)) { 272162306a36Sopenharmony_ci spec->precision = clamp(prec, 0, PRECISION_MAX); 272262306a36Sopenharmony_ci } 272362306a36Sopenharmony_ci} 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_ci/** 272662306a36Sopenharmony_ci * vsnprintf - Format a string and place it in a buffer 272762306a36Sopenharmony_ci * @buf: The buffer to place the result into 272862306a36Sopenharmony_ci * @size: The size of the buffer, including the trailing null space 272962306a36Sopenharmony_ci * @fmt: The format string to use 273062306a36Sopenharmony_ci * @args: Arguments for the format string 273162306a36Sopenharmony_ci * 273262306a36Sopenharmony_ci * This function generally follows C99 vsnprintf, but has some 273362306a36Sopenharmony_ci * extensions and a few limitations: 273462306a36Sopenharmony_ci * 273562306a36Sopenharmony_ci * - ``%n`` is unsupported 273662306a36Sopenharmony_ci * - ``%p*`` is handled by pointer() 273762306a36Sopenharmony_ci * 273862306a36Sopenharmony_ci * See pointer() or Documentation/core-api/printk-formats.rst for more 273962306a36Sopenharmony_ci * extensive description. 274062306a36Sopenharmony_ci * 274162306a36Sopenharmony_ci * **Please update the documentation in both places when making changes** 274262306a36Sopenharmony_ci * 274362306a36Sopenharmony_ci * The return value is the number of characters which would 274462306a36Sopenharmony_ci * be generated for the given input, excluding the trailing 274562306a36Sopenharmony_ci * '\0', as per ISO C99. If you want to have the exact 274662306a36Sopenharmony_ci * number of characters written into @buf as return value 274762306a36Sopenharmony_ci * (not including the trailing '\0'), use vscnprintf(). If the 274862306a36Sopenharmony_ci * return is greater than or equal to @size, the resulting 274962306a36Sopenharmony_ci * string is truncated. 275062306a36Sopenharmony_ci * 275162306a36Sopenharmony_ci * If you're not already dealing with a va_list consider using snprintf(). 275262306a36Sopenharmony_ci */ 275362306a36Sopenharmony_ciint vsnprintf(char *buf, size_t size, const char *fmt, va_list args) 275462306a36Sopenharmony_ci{ 275562306a36Sopenharmony_ci unsigned long long num; 275662306a36Sopenharmony_ci char *str, *end; 275762306a36Sopenharmony_ci struct printf_spec spec = {0}; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci /* Reject out-of-range values early. Large positive sizes are 276062306a36Sopenharmony_ci used for unknown buffer sizes. */ 276162306a36Sopenharmony_ci if (WARN_ON_ONCE(size > INT_MAX)) 276262306a36Sopenharmony_ci return 0; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci str = buf; 276562306a36Sopenharmony_ci end = buf + size; 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci /* Make sure end is always >= buf */ 276862306a36Sopenharmony_ci if (end < buf) { 276962306a36Sopenharmony_ci end = ((void *)-1); 277062306a36Sopenharmony_ci size = end - buf; 277162306a36Sopenharmony_ci } 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci while (*fmt) { 277462306a36Sopenharmony_ci const char *old_fmt = fmt; 277562306a36Sopenharmony_ci int read = format_decode(fmt, &spec); 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci fmt += read; 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci switch (spec.type) { 278062306a36Sopenharmony_ci case FORMAT_TYPE_NONE: { 278162306a36Sopenharmony_ci int copy = read; 278262306a36Sopenharmony_ci if (str < end) { 278362306a36Sopenharmony_ci if (copy > end - str) 278462306a36Sopenharmony_ci copy = end - str; 278562306a36Sopenharmony_ci memcpy(str, old_fmt, copy); 278662306a36Sopenharmony_ci } 278762306a36Sopenharmony_ci str += read; 278862306a36Sopenharmony_ci break; 278962306a36Sopenharmony_ci } 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci case FORMAT_TYPE_WIDTH: 279262306a36Sopenharmony_ci set_field_width(&spec, va_arg(args, int)); 279362306a36Sopenharmony_ci break; 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci case FORMAT_TYPE_PRECISION: 279662306a36Sopenharmony_ci set_precision(&spec, va_arg(args, int)); 279762306a36Sopenharmony_ci break; 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci case FORMAT_TYPE_CHAR: { 280062306a36Sopenharmony_ci char c; 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci if (!(spec.flags & LEFT)) { 280362306a36Sopenharmony_ci while (--spec.field_width > 0) { 280462306a36Sopenharmony_ci if (str < end) 280562306a36Sopenharmony_ci *str = ' '; 280662306a36Sopenharmony_ci ++str; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci } 280962306a36Sopenharmony_ci } 281062306a36Sopenharmony_ci c = (unsigned char) va_arg(args, int); 281162306a36Sopenharmony_ci if (str < end) 281262306a36Sopenharmony_ci *str = c; 281362306a36Sopenharmony_ci ++str; 281462306a36Sopenharmony_ci while (--spec.field_width > 0) { 281562306a36Sopenharmony_ci if (str < end) 281662306a36Sopenharmony_ci *str = ' '; 281762306a36Sopenharmony_ci ++str; 281862306a36Sopenharmony_ci } 281962306a36Sopenharmony_ci break; 282062306a36Sopenharmony_ci } 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci case FORMAT_TYPE_STR: 282362306a36Sopenharmony_ci str = string(str, end, va_arg(args, char *), spec); 282462306a36Sopenharmony_ci break; 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci case FORMAT_TYPE_PTR: 282762306a36Sopenharmony_ci str = pointer(fmt, str, end, va_arg(args, void *), 282862306a36Sopenharmony_ci spec); 282962306a36Sopenharmony_ci while (isalnum(*fmt)) 283062306a36Sopenharmony_ci fmt++; 283162306a36Sopenharmony_ci break; 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci case FORMAT_TYPE_PERCENT_CHAR: 283462306a36Sopenharmony_ci if (str < end) 283562306a36Sopenharmony_ci *str = '%'; 283662306a36Sopenharmony_ci ++str; 283762306a36Sopenharmony_ci break; 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_ci case FORMAT_TYPE_INVALID: 284062306a36Sopenharmony_ci /* 284162306a36Sopenharmony_ci * Presumably the arguments passed gcc's type 284262306a36Sopenharmony_ci * checking, but there is no safe or sane way 284362306a36Sopenharmony_ci * for us to continue parsing the format and 284462306a36Sopenharmony_ci * fetching from the va_list; the remaining 284562306a36Sopenharmony_ci * specifiers and arguments would be out of 284662306a36Sopenharmony_ci * sync. 284762306a36Sopenharmony_ci */ 284862306a36Sopenharmony_ci goto out; 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci default: 285162306a36Sopenharmony_ci switch (spec.type) { 285262306a36Sopenharmony_ci case FORMAT_TYPE_LONG_LONG: 285362306a36Sopenharmony_ci num = va_arg(args, long long); 285462306a36Sopenharmony_ci break; 285562306a36Sopenharmony_ci case FORMAT_TYPE_ULONG: 285662306a36Sopenharmony_ci num = va_arg(args, unsigned long); 285762306a36Sopenharmony_ci break; 285862306a36Sopenharmony_ci case FORMAT_TYPE_LONG: 285962306a36Sopenharmony_ci num = va_arg(args, long); 286062306a36Sopenharmony_ci break; 286162306a36Sopenharmony_ci case FORMAT_TYPE_SIZE_T: 286262306a36Sopenharmony_ci if (spec.flags & SIGN) 286362306a36Sopenharmony_ci num = va_arg(args, ssize_t); 286462306a36Sopenharmony_ci else 286562306a36Sopenharmony_ci num = va_arg(args, size_t); 286662306a36Sopenharmony_ci break; 286762306a36Sopenharmony_ci case FORMAT_TYPE_PTRDIFF: 286862306a36Sopenharmony_ci num = va_arg(args, ptrdiff_t); 286962306a36Sopenharmony_ci break; 287062306a36Sopenharmony_ci case FORMAT_TYPE_UBYTE: 287162306a36Sopenharmony_ci num = (unsigned char) va_arg(args, int); 287262306a36Sopenharmony_ci break; 287362306a36Sopenharmony_ci case FORMAT_TYPE_BYTE: 287462306a36Sopenharmony_ci num = (signed char) va_arg(args, int); 287562306a36Sopenharmony_ci break; 287662306a36Sopenharmony_ci case FORMAT_TYPE_USHORT: 287762306a36Sopenharmony_ci num = (unsigned short) va_arg(args, int); 287862306a36Sopenharmony_ci break; 287962306a36Sopenharmony_ci case FORMAT_TYPE_SHORT: 288062306a36Sopenharmony_ci num = (short) va_arg(args, int); 288162306a36Sopenharmony_ci break; 288262306a36Sopenharmony_ci case FORMAT_TYPE_INT: 288362306a36Sopenharmony_ci num = (int) va_arg(args, int); 288462306a36Sopenharmony_ci break; 288562306a36Sopenharmony_ci default: 288662306a36Sopenharmony_ci num = va_arg(args, unsigned int); 288762306a36Sopenharmony_ci } 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci str = number(str, end, num, spec); 289062306a36Sopenharmony_ci } 289162306a36Sopenharmony_ci } 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ciout: 289462306a36Sopenharmony_ci if (size > 0) { 289562306a36Sopenharmony_ci if (str < end) 289662306a36Sopenharmony_ci *str = '\0'; 289762306a36Sopenharmony_ci else 289862306a36Sopenharmony_ci end[-1] = '\0'; 289962306a36Sopenharmony_ci } 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci /* the trailing null byte doesn't count towards the total */ 290262306a36Sopenharmony_ci return str-buf; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci} 290562306a36Sopenharmony_ciEXPORT_SYMBOL(vsnprintf); 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci/** 290862306a36Sopenharmony_ci * vscnprintf - Format a string and place it in a buffer 290962306a36Sopenharmony_ci * @buf: The buffer to place the result into 291062306a36Sopenharmony_ci * @size: The size of the buffer, including the trailing null space 291162306a36Sopenharmony_ci * @fmt: The format string to use 291262306a36Sopenharmony_ci * @args: Arguments for the format string 291362306a36Sopenharmony_ci * 291462306a36Sopenharmony_ci * The return value is the number of characters which have been written into 291562306a36Sopenharmony_ci * the @buf not including the trailing '\0'. If @size is == 0 the function 291662306a36Sopenharmony_ci * returns 0. 291762306a36Sopenharmony_ci * 291862306a36Sopenharmony_ci * If you're not already dealing with a va_list consider using scnprintf(). 291962306a36Sopenharmony_ci * 292062306a36Sopenharmony_ci * See the vsnprintf() documentation for format string extensions over C99. 292162306a36Sopenharmony_ci */ 292262306a36Sopenharmony_ciint vscnprintf(char *buf, size_t size, const char *fmt, va_list args) 292362306a36Sopenharmony_ci{ 292462306a36Sopenharmony_ci int i; 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci if (unlikely(!size)) 292762306a36Sopenharmony_ci return 0; 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci i = vsnprintf(buf, size, fmt, args); 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci if (likely(i < size)) 293262306a36Sopenharmony_ci return i; 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_ci return size - 1; 293562306a36Sopenharmony_ci} 293662306a36Sopenharmony_ciEXPORT_SYMBOL(vscnprintf); 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci/** 293962306a36Sopenharmony_ci * snprintf - Format a string and place it in a buffer 294062306a36Sopenharmony_ci * @buf: The buffer to place the result into 294162306a36Sopenharmony_ci * @size: The size of the buffer, including the trailing null space 294262306a36Sopenharmony_ci * @fmt: The format string to use 294362306a36Sopenharmony_ci * @...: Arguments for the format string 294462306a36Sopenharmony_ci * 294562306a36Sopenharmony_ci * The return value is the number of characters which would be 294662306a36Sopenharmony_ci * generated for the given input, excluding the trailing null, 294762306a36Sopenharmony_ci * as per ISO C99. If the return is greater than or equal to 294862306a36Sopenharmony_ci * @size, the resulting string is truncated. 294962306a36Sopenharmony_ci * 295062306a36Sopenharmony_ci * See the vsnprintf() documentation for format string extensions over C99. 295162306a36Sopenharmony_ci */ 295262306a36Sopenharmony_ciint snprintf(char *buf, size_t size, const char *fmt, ...) 295362306a36Sopenharmony_ci{ 295462306a36Sopenharmony_ci va_list args; 295562306a36Sopenharmony_ci int i; 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci va_start(args, fmt); 295862306a36Sopenharmony_ci i = vsnprintf(buf, size, fmt, args); 295962306a36Sopenharmony_ci va_end(args); 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci return i; 296262306a36Sopenharmony_ci} 296362306a36Sopenharmony_ciEXPORT_SYMBOL(snprintf); 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci/** 296662306a36Sopenharmony_ci * scnprintf - Format a string and place it in a buffer 296762306a36Sopenharmony_ci * @buf: The buffer to place the result into 296862306a36Sopenharmony_ci * @size: The size of the buffer, including the trailing null space 296962306a36Sopenharmony_ci * @fmt: The format string to use 297062306a36Sopenharmony_ci * @...: Arguments for the format string 297162306a36Sopenharmony_ci * 297262306a36Sopenharmony_ci * The return value is the number of characters written into @buf not including 297362306a36Sopenharmony_ci * the trailing '\0'. If @size is == 0 the function returns 0. 297462306a36Sopenharmony_ci */ 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ciint scnprintf(char *buf, size_t size, const char *fmt, ...) 297762306a36Sopenharmony_ci{ 297862306a36Sopenharmony_ci va_list args; 297962306a36Sopenharmony_ci int i; 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci va_start(args, fmt); 298262306a36Sopenharmony_ci i = vscnprintf(buf, size, fmt, args); 298362306a36Sopenharmony_ci va_end(args); 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci return i; 298662306a36Sopenharmony_ci} 298762306a36Sopenharmony_ciEXPORT_SYMBOL(scnprintf); 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci/** 299062306a36Sopenharmony_ci * vsprintf - Format a string and place it in a buffer 299162306a36Sopenharmony_ci * @buf: The buffer to place the result into 299262306a36Sopenharmony_ci * @fmt: The format string to use 299362306a36Sopenharmony_ci * @args: Arguments for the format string 299462306a36Sopenharmony_ci * 299562306a36Sopenharmony_ci * The function returns the number of characters written 299662306a36Sopenharmony_ci * into @buf. Use vsnprintf() or vscnprintf() in order to avoid 299762306a36Sopenharmony_ci * buffer overflows. 299862306a36Sopenharmony_ci * 299962306a36Sopenharmony_ci * If you're not already dealing with a va_list consider using sprintf(). 300062306a36Sopenharmony_ci * 300162306a36Sopenharmony_ci * See the vsnprintf() documentation for format string extensions over C99. 300262306a36Sopenharmony_ci */ 300362306a36Sopenharmony_ciint vsprintf(char *buf, const char *fmt, va_list args) 300462306a36Sopenharmony_ci{ 300562306a36Sopenharmony_ci return vsnprintf(buf, INT_MAX, fmt, args); 300662306a36Sopenharmony_ci} 300762306a36Sopenharmony_ciEXPORT_SYMBOL(vsprintf); 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci/** 301062306a36Sopenharmony_ci * sprintf - Format a string and place it in a buffer 301162306a36Sopenharmony_ci * @buf: The buffer to place the result into 301262306a36Sopenharmony_ci * @fmt: The format string to use 301362306a36Sopenharmony_ci * @...: Arguments for the format string 301462306a36Sopenharmony_ci * 301562306a36Sopenharmony_ci * The function returns the number of characters written 301662306a36Sopenharmony_ci * into @buf. Use snprintf() or scnprintf() in order to avoid 301762306a36Sopenharmony_ci * buffer overflows. 301862306a36Sopenharmony_ci * 301962306a36Sopenharmony_ci * See the vsnprintf() documentation for format string extensions over C99. 302062306a36Sopenharmony_ci */ 302162306a36Sopenharmony_ciint sprintf(char *buf, const char *fmt, ...) 302262306a36Sopenharmony_ci{ 302362306a36Sopenharmony_ci va_list args; 302462306a36Sopenharmony_ci int i; 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_ci va_start(args, fmt); 302762306a36Sopenharmony_ci i = vsnprintf(buf, INT_MAX, fmt, args); 302862306a36Sopenharmony_ci va_end(args); 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci return i; 303162306a36Sopenharmony_ci} 303262306a36Sopenharmony_ciEXPORT_SYMBOL(sprintf); 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci#ifdef CONFIG_BINARY_PRINTF 303562306a36Sopenharmony_ci/* 303662306a36Sopenharmony_ci * bprintf service: 303762306a36Sopenharmony_ci * vbin_printf() - VA arguments to binary data 303862306a36Sopenharmony_ci * bstr_printf() - Binary data to text string 303962306a36Sopenharmony_ci */ 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ci/** 304262306a36Sopenharmony_ci * vbin_printf - Parse a format string and place args' binary value in a buffer 304362306a36Sopenharmony_ci * @bin_buf: The buffer to place args' binary value 304462306a36Sopenharmony_ci * @size: The size of the buffer(by words(32bits), not characters) 304562306a36Sopenharmony_ci * @fmt: The format string to use 304662306a36Sopenharmony_ci * @args: Arguments for the format string 304762306a36Sopenharmony_ci * 304862306a36Sopenharmony_ci * The format follows C99 vsnprintf, except %n is ignored, and its argument 304962306a36Sopenharmony_ci * is skipped. 305062306a36Sopenharmony_ci * 305162306a36Sopenharmony_ci * The return value is the number of words(32bits) which would be generated for 305262306a36Sopenharmony_ci * the given input. 305362306a36Sopenharmony_ci * 305462306a36Sopenharmony_ci * NOTE: 305562306a36Sopenharmony_ci * If the return value is greater than @size, the resulting bin_buf is NOT 305662306a36Sopenharmony_ci * valid for bstr_printf(). 305762306a36Sopenharmony_ci */ 305862306a36Sopenharmony_ciint vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) 305962306a36Sopenharmony_ci{ 306062306a36Sopenharmony_ci struct printf_spec spec = {0}; 306162306a36Sopenharmony_ci char *str, *end; 306262306a36Sopenharmony_ci int width; 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci str = (char *)bin_buf; 306562306a36Sopenharmony_ci end = (char *)(bin_buf + size); 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci#define save_arg(type) \ 306862306a36Sopenharmony_ci({ \ 306962306a36Sopenharmony_ci unsigned long long value; \ 307062306a36Sopenharmony_ci if (sizeof(type) == 8) { \ 307162306a36Sopenharmony_ci unsigned long long val8; \ 307262306a36Sopenharmony_ci str = PTR_ALIGN(str, sizeof(u32)); \ 307362306a36Sopenharmony_ci val8 = va_arg(args, unsigned long long); \ 307462306a36Sopenharmony_ci if (str + sizeof(type) <= end) { \ 307562306a36Sopenharmony_ci *(u32 *)str = *(u32 *)&val8; \ 307662306a36Sopenharmony_ci *(u32 *)(str + 4) = *((u32 *)&val8 + 1); \ 307762306a36Sopenharmony_ci } \ 307862306a36Sopenharmony_ci value = val8; \ 307962306a36Sopenharmony_ci } else { \ 308062306a36Sopenharmony_ci unsigned int val4; \ 308162306a36Sopenharmony_ci str = PTR_ALIGN(str, sizeof(type)); \ 308262306a36Sopenharmony_ci val4 = va_arg(args, int); \ 308362306a36Sopenharmony_ci if (str + sizeof(type) <= end) \ 308462306a36Sopenharmony_ci *(typeof(type) *)str = (type)(long)val4; \ 308562306a36Sopenharmony_ci value = (unsigned long long)val4; \ 308662306a36Sopenharmony_ci } \ 308762306a36Sopenharmony_ci str += sizeof(type); \ 308862306a36Sopenharmony_ci value; \ 308962306a36Sopenharmony_ci}) 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci while (*fmt) { 309262306a36Sopenharmony_ci int read = format_decode(fmt, &spec); 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci fmt += read; 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci switch (spec.type) { 309762306a36Sopenharmony_ci case FORMAT_TYPE_NONE: 309862306a36Sopenharmony_ci case FORMAT_TYPE_PERCENT_CHAR: 309962306a36Sopenharmony_ci break; 310062306a36Sopenharmony_ci case FORMAT_TYPE_INVALID: 310162306a36Sopenharmony_ci goto out; 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci case FORMAT_TYPE_WIDTH: 310462306a36Sopenharmony_ci case FORMAT_TYPE_PRECISION: 310562306a36Sopenharmony_ci width = (int)save_arg(int); 310662306a36Sopenharmony_ci /* Pointers may require the width */ 310762306a36Sopenharmony_ci if (*fmt == 'p') 310862306a36Sopenharmony_ci set_field_width(&spec, width); 310962306a36Sopenharmony_ci break; 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci case FORMAT_TYPE_CHAR: 311262306a36Sopenharmony_ci save_arg(char); 311362306a36Sopenharmony_ci break; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci case FORMAT_TYPE_STR: { 311662306a36Sopenharmony_ci const char *save_str = va_arg(args, char *); 311762306a36Sopenharmony_ci const char *err_msg; 311862306a36Sopenharmony_ci size_t len; 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci err_msg = check_pointer_msg(save_str); 312162306a36Sopenharmony_ci if (err_msg) 312262306a36Sopenharmony_ci save_str = err_msg; 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci len = strlen(save_str) + 1; 312562306a36Sopenharmony_ci if (str + len < end) 312662306a36Sopenharmony_ci memcpy(str, save_str, len); 312762306a36Sopenharmony_ci str += len; 312862306a36Sopenharmony_ci break; 312962306a36Sopenharmony_ci } 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci case FORMAT_TYPE_PTR: 313262306a36Sopenharmony_ci /* Dereferenced pointers must be done now */ 313362306a36Sopenharmony_ci switch (*fmt) { 313462306a36Sopenharmony_ci /* Dereference of functions is still OK */ 313562306a36Sopenharmony_ci case 'S': 313662306a36Sopenharmony_ci case 's': 313762306a36Sopenharmony_ci case 'x': 313862306a36Sopenharmony_ci case 'K': 313962306a36Sopenharmony_ci case 'e': 314062306a36Sopenharmony_ci save_arg(void *); 314162306a36Sopenharmony_ci break; 314262306a36Sopenharmony_ci default: 314362306a36Sopenharmony_ci if (!isalnum(*fmt)) { 314462306a36Sopenharmony_ci save_arg(void *); 314562306a36Sopenharmony_ci break; 314662306a36Sopenharmony_ci } 314762306a36Sopenharmony_ci str = pointer(fmt, str, end, va_arg(args, void *), 314862306a36Sopenharmony_ci spec); 314962306a36Sopenharmony_ci if (str + 1 < end) 315062306a36Sopenharmony_ci *str++ = '\0'; 315162306a36Sopenharmony_ci else 315262306a36Sopenharmony_ci end[-1] = '\0'; /* Must be nul terminated */ 315362306a36Sopenharmony_ci } 315462306a36Sopenharmony_ci /* skip all alphanumeric pointer suffixes */ 315562306a36Sopenharmony_ci while (isalnum(*fmt)) 315662306a36Sopenharmony_ci fmt++; 315762306a36Sopenharmony_ci break; 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci default: 316062306a36Sopenharmony_ci switch (spec.type) { 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_ci case FORMAT_TYPE_LONG_LONG: 316362306a36Sopenharmony_ci save_arg(long long); 316462306a36Sopenharmony_ci break; 316562306a36Sopenharmony_ci case FORMAT_TYPE_ULONG: 316662306a36Sopenharmony_ci case FORMAT_TYPE_LONG: 316762306a36Sopenharmony_ci save_arg(unsigned long); 316862306a36Sopenharmony_ci break; 316962306a36Sopenharmony_ci case FORMAT_TYPE_SIZE_T: 317062306a36Sopenharmony_ci save_arg(size_t); 317162306a36Sopenharmony_ci break; 317262306a36Sopenharmony_ci case FORMAT_TYPE_PTRDIFF: 317362306a36Sopenharmony_ci save_arg(ptrdiff_t); 317462306a36Sopenharmony_ci break; 317562306a36Sopenharmony_ci case FORMAT_TYPE_UBYTE: 317662306a36Sopenharmony_ci case FORMAT_TYPE_BYTE: 317762306a36Sopenharmony_ci save_arg(char); 317862306a36Sopenharmony_ci break; 317962306a36Sopenharmony_ci case FORMAT_TYPE_USHORT: 318062306a36Sopenharmony_ci case FORMAT_TYPE_SHORT: 318162306a36Sopenharmony_ci save_arg(short); 318262306a36Sopenharmony_ci break; 318362306a36Sopenharmony_ci default: 318462306a36Sopenharmony_ci save_arg(int); 318562306a36Sopenharmony_ci } 318662306a36Sopenharmony_ci } 318762306a36Sopenharmony_ci } 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ciout: 319062306a36Sopenharmony_ci return (u32 *)(PTR_ALIGN(str, sizeof(u32))) - bin_buf; 319162306a36Sopenharmony_ci#undef save_arg 319262306a36Sopenharmony_ci} 319362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vbin_printf); 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci/** 319662306a36Sopenharmony_ci * bstr_printf - Format a string from binary arguments and place it in a buffer 319762306a36Sopenharmony_ci * @buf: The buffer to place the result into 319862306a36Sopenharmony_ci * @size: The size of the buffer, including the trailing null space 319962306a36Sopenharmony_ci * @fmt: The format string to use 320062306a36Sopenharmony_ci * @bin_buf: Binary arguments for the format string 320162306a36Sopenharmony_ci * 320262306a36Sopenharmony_ci * This function like C99 vsnprintf, but the difference is that vsnprintf gets 320362306a36Sopenharmony_ci * arguments from stack, and bstr_printf gets arguments from @bin_buf which is 320462306a36Sopenharmony_ci * a binary buffer that generated by vbin_printf. 320562306a36Sopenharmony_ci * 320662306a36Sopenharmony_ci * The format follows C99 vsnprintf, but has some extensions: 320762306a36Sopenharmony_ci * see vsnprintf comment for details. 320862306a36Sopenharmony_ci * 320962306a36Sopenharmony_ci * The return value is the number of characters which would 321062306a36Sopenharmony_ci * be generated for the given input, excluding the trailing 321162306a36Sopenharmony_ci * '\0', as per ISO C99. If you want to have the exact 321262306a36Sopenharmony_ci * number of characters written into @buf as return value 321362306a36Sopenharmony_ci * (not including the trailing '\0'), use vscnprintf(). If the 321462306a36Sopenharmony_ci * return is greater than or equal to @size, the resulting 321562306a36Sopenharmony_ci * string is truncated. 321662306a36Sopenharmony_ci */ 321762306a36Sopenharmony_ciint bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) 321862306a36Sopenharmony_ci{ 321962306a36Sopenharmony_ci struct printf_spec spec = {0}; 322062306a36Sopenharmony_ci char *str, *end; 322162306a36Sopenharmony_ci const char *args = (const char *)bin_buf; 322262306a36Sopenharmony_ci 322362306a36Sopenharmony_ci if (WARN_ON_ONCE(size > INT_MAX)) 322462306a36Sopenharmony_ci return 0; 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci str = buf; 322762306a36Sopenharmony_ci end = buf + size; 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci#define get_arg(type) \ 323062306a36Sopenharmony_ci({ \ 323162306a36Sopenharmony_ci typeof(type) value; \ 323262306a36Sopenharmony_ci if (sizeof(type) == 8) { \ 323362306a36Sopenharmony_ci args = PTR_ALIGN(args, sizeof(u32)); \ 323462306a36Sopenharmony_ci *(u32 *)&value = *(u32 *)args; \ 323562306a36Sopenharmony_ci *((u32 *)&value + 1) = *(u32 *)(args + 4); \ 323662306a36Sopenharmony_ci } else { \ 323762306a36Sopenharmony_ci args = PTR_ALIGN(args, sizeof(type)); \ 323862306a36Sopenharmony_ci value = *(typeof(type) *)args; \ 323962306a36Sopenharmony_ci } \ 324062306a36Sopenharmony_ci args += sizeof(type); \ 324162306a36Sopenharmony_ci value; \ 324262306a36Sopenharmony_ci}) 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci /* Make sure end is always >= buf */ 324562306a36Sopenharmony_ci if (end < buf) { 324662306a36Sopenharmony_ci end = ((void *)-1); 324762306a36Sopenharmony_ci size = end - buf; 324862306a36Sopenharmony_ci } 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci while (*fmt) { 325162306a36Sopenharmony_ci const char *old_fmt = fmt; 325262306a36Sopenharmony_ci int read = format_decode(fmt, &spec); 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci fmt += read; 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci switch (spec.type) { 325762306a36Sopenharmony_ci case FORMAT_TYPE_NONE: { 325862306a36Sopenharmony_ci int copy = read; 325962306a36Sopenharmony_ci if (str < end) { 326062306a36Sopenharmony_ci if (copy > end - str) 326162306a36Sopenharmony_ci copy = end - str; 326262306a36Sopenharmony_ci memcpy(str, old_fmt, copy); 326362306a36Sopenharmony_ci } 326462306a36Sopenharmony_ci str += read; 326562306a36Sopenharmony_ci break; 326662306a36Sopenharmony_ci } 326762306a36Sopenharmony_ci 326862306a36Sopenharmony_ci case FORMAT_TYPE_WIDTH: 326962306a36Sopenharmony_ci set_field_width(&spec, get_arg(int)); 327062306a36Sopenharmony_ci break; 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci case FORMAT_TYPE_PRECISION: 327362306a36Sopenharmony_ci set_precision(&spec, get_arg(int)); 327462306a36Sopenharmony_ci break; 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ci case FORMAT_TYPE_CHAR: { 327762306a36Sopenharmony_ci char c; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci if (!(spec.flags & LEFT)) { 328062306a36Sopenharmony_ci while (--spec.field_width > 0) { 328162306a36Sopenharmony_ci if (str < end) 328262306a36Sopenharmony_ci *str = ' '; 328362306a36Sopenharmony_ci ++str; 328462306a36Sopenharmony_ci } 328562306a36Sopenharmony_ci } 328662306a36Sopenharmony_ci c = (unsigned char) get_arg(char); 328762306a36Sopenharmony_ci if (str < end) 328862306a36Sopenharmony_ci *str = c; 328962306a36Sopenharmony_ci ++str; 329062306a36Sopenharmony_ci while (--spec.field_width > 0) { 329162306a36Sopenharmony_ci if (str < end) 329262306a36Sopenharmony_ci *str = ' '; 329362306a36Sopenharmony_ci ++str; 329462306a36Sopenharmony_ci } 329562306a36Sopenharmony_ci break; 329662306a36Sopenharmony_ci } 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci case FORMAT_TYPE_STR: { 329962306a36Sopenharmony_ci const char *str_arg = args; 330062306a36Sopenharmony_ci args += strlen(str_arg) + 1; 330162306a36Sopenharmony_ci str = string(str, end, (char *)str_arg, spec); 330262306a36Sopenharmony_ci break; 330362306a36Sopenharmony_ci } 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci case FORMAT_TYPE_PTR: { 330662306a36Sopenharmony_ci bool process = false; 330762306a36Sopenharmony_ci int copy, len; 330862306a36Sopenharmony_ci /* Non function dereferences were already done */ 330962306a36Sopenharmony_ci switch (*fmt) { 331062306a36Sopenharmony_ci case 'S': 331162306a36Sopenharmony_ci case 's': 331262306a36Sopenharmony_ci case 'x': 331362306a36Sopenharmony_ci case 'K': 331462306a36Sopenharmony_ci case 'e': 331562306a36Sopenharmony_ci process = true; 331662306a36Sopenharmony_ci break; 331762306a36Sopenharmony_ci default: 331862306a36Sopenharmony_ci if (!isalnum(*fmt)) { 331962306a36Sopenharmony_ci process = true; 332062306a36Sopenharmony_ci break; 332162306a36Sopenharmony_ci } 332262306a36Sopenharmony_ci /* Pointer dereference was already processed */ 332362306a36Sopenharmony_ci if (str < end) { 332462306a36Sopenharmony_ci len = copy = strlen(args); 332562306a36Sopenharmony_ci if (copy > end - str) 332662306a36Sopenharmony_ci copy = end - str; 332762306a36Sopenharmony_ci memcpy(str, args, copy); 332862306a36Sopenharmony_ci str += len; 332962306a36Sopenharmony_ci args += len + 1; 333062306a36Sopenharmony_ci } 333162306a36Sopenharmony_ci } 333262306a36Sopenharmony_ci if (process) 333362306a36Sopenharmony_ci str = pointer(fmt, str, end, get_arg(void *), spec); 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci while (isalnum(*fmt)) 333662306a36Sopenharmony_ci fmt++; 333762306a36Sopenharmony_ci break; 333862306a36Sopenharmony_ci } 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci case FORMAT_TYPE_PERCENT_CHAR: 334162306a36Sopenharmony_ci if (str < end) 334262306a36Sopenharmony_ci *str = '%'; 334362306a36Sopenharmony_ci ++str; 334462306a36Sopenharmony_ci break; 334562306a36Sopenharmony_ci 334662306a36Sopenharmony_ci case FORMAT_TYPE_INVALID: 334762306a36Sopenharmony_ci goto out; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci default: { 335062306a36Sopenharmony_ci unsigned long long num; 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci switch (spec.type) { 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci case FORMAT_TYPE_LONG_LONG: 335562306a36Sopenharmony_ci num = get_arg(long long); 335662306a36Sopenharmony_ci break; 335762306a36Sopenharmony_ci case FORMAT_TYPE_ULONG: 335862306a36Sopenharmony_ci case FORMAT_TYPE_LONG: 335962306a36Sopenharmony_ci num = get_arg(unsigned long); 336062306a36Sopenharmony_ci break; 336162306a36Sopenharmony_ci case FORMAT_TYPE_SIZE_T: 336262306a36Sopenharmony_ci num = get_arg(size_t); 336362306a36Sopenharmony_ci break; 336462306a36Sopenharmony_ci case FORMAT_TYPE_PTRDIFF: 336562306a36Sopenharmony_ci num = get_arg(ptrdiff_t); 336662306a36Sopenharmony_ci break; 336762306a36Sopenharmony_ci case FORMAT_TYPE_UBYTE: 336862306a36Sopenharmony_ci num = get_arg(unsigned char); 336962306a36Sopenharmony_ci break; 337062306a36Sopenharmony_ci case FORMAT_TYPE_BYTE: 337162306a36Sopenharmony_ci num = get_arg(signed char); 337262306a36Sopenharmony_ci break; 337362306a36Sopenharmony_ci case FORMAT_TYPE_USHORT: 337462306a36Sopenharmony_ci num = get_arg(unsigned short); 337562306a36Sopenharmony_ci break; 337662306a36Sopenharmony_ci case FORMAT_TYPE_SHORT: 337762306a36Sopenharmony_ci num = get_arg(short); 337862306a36Sopenharmony_ci break; 337962306a36Sopenharmony_ci case FORMAT_TYPE_UINT: 338062306a36Sopenharmony_ci num = get_arg(unsigned int); 338162306a36Sopenharmony_ci break; 338262306a36Sopenharmony_ci default: 338362306a36Sopenharmony_ci num = get_arg(int); 338462306a36Sopenharmony_ci } 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci str = number(str, end, num, spec); 338762306a36Sopenharmony_ci } /* default: */ 338862306a36Sopenharmony_ci } /* switch(spec.type) */ 338962306a36Sopenharmony_ci } /* while(*fmt) */ 339062306a36Sopenharmony_ci 339162306a36Sopenharmony_ciout: 339262306a36Sopenharmony_ci if (size > 0) { 339362306a36Sopenharmony_ci if (str < end) 339462306a36Sopenharmony_ci *str = '\0'; 339562306a36Sopenharmony_ci else 339662306a36Sopenharmony_ci end[-1] = '\0'; 339762306a36Sopenharmony_ci } 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci#undef get_arg 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci /* the trailing null byte doesn't count towards the total */ 340262306a36Sopenharmony_ci return str - buf; 340362306a36Sopenharmony_ci} 340462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bstr_printf); 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci/** 340762306a36Sopenharmony_ci * bprintf - Parse a format string and place args' binary value in a buffer 340862306a36Sopenharmony_ci * @bin_buf: The buffer to place args' binary value 340962306a36Sopenharmony_ci * @size: The size of the buffer(by words(32bits), not characters) 341062306a36Sopenharmony_ci * @fmt: The format string to use 341162306a36Sopenharmony_ci * @...: Arguments for the format string 341262306a36Sopenharmony_ci * 341362306a36Sopenharmony_ci * The function returns the number of words(u32) written 341462306a36Sopenharmony_ci * into @bin_buf. 341562306a36Sopenharmony_ci */ 341662306a36Sopenharmony_ciint bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) 341762306a36Sopenharmony_ci{ 341862306a36Sopenharmony_ci va_list args; 341962306a36Sopenharmony_ci int ret; 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci va_start(args, fmt); 342262306a36Sopenharmony_ci ret = vbin_printf(bin_buf, size, fmt, args); 342362306a36Sopenharmony_ci va_end(args); 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci return ret; 342662306a36Sopenharmony_ci} 342762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bprintf); 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci#endif /* CONFIG_BINARY_PRINTF */ 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci/** 343262306a36Sopenharmony_ci * vsscanf - Unformat a buffer into a list of arguments 343362306a36Sopenharmony_ci * @buf: input buffer 343462306a36Sopenharmony_ci * @fmt: format of buffer 343562306a36Sopenharmony_ci * @args: arguments 343662306a36Sopenharmony_ci */ 343762306a36Sopenharmony_ciint vsscanf(const char *buf, const char *fmt, va_list args) 343862306a36Sopenharmony_ci{ 343962306a36Sopenharmony_ci const char *str = buf; 344062306a36Sopenharmony_ci char *next; 344162306a36Sopenharmony_ci char digit; 344262306a36Sopenharmony_ci int num = 0; 344362306a36Sopenharmony_ci u8 qualifier; 344462306a36Sopenharmony_ci unsigned int base; 344562306a36Sopenharmony_ci union { 344662306a36Sopenharmony_ci long long s; 344762306a36Sopenharmony_ci unsigned long long u; 344862306a36Sopenharmony_ci } val; 344962306a36Sopenharmony_ci s16 field_width; 345062306a36Sopenharmony_ci bool is_sign; 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci while (*fmt) { 345362306a36Sopenharmony_ci /* skip any white space in format */ 345462306a36Sopenharmony_ci /* white space in format matches any amount of 345562306a36Sopenharmony_ci * white space, including none, in the input. 345662306a36Sopenharmony_ci */ 345762306a36Sopenharmony_ci if (isspace(*fmt)) { 345862306a36Sopenharmony_ci fmt = skip_spaces(++fmt); 345962306a36Sopenharmony_ci str = skip_spaces(str); 346062306a36Sopenharmony_ci } 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci /* anything that is not a conversion must match exactly */ 346362306a36Sopenharmony_ci if (*fmt != '%' && *fmt) { 346462306a36Sopenharmony_ci if (*fmt++ != *str++) 346562306a36Sopenharmony_ci break; 346662306a36Sopenharmony_ci continue; 346762306a36Sopenharmony_ci } 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci if (!*fmt) 347062306a36Sopenharmony_ci break; 347162306a36Sopenharmony_ci ++fmt; 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci /* skip this conversion. 347462306a36Sopenharmony_ci * advance both strings to next white space 347562306a36Sopenharmony_ci */ 347662306a36Sopenharmony_ci if (*fmt == '*') { 347762306a36Sopenharmony_ci if (!*str) 347862306a36Sopenharmony_ci break; 347962306a36Sopenharmony_ci while (!isspace(*fmt) && *fmt != '%' && *fmt) { 348062306a36Sopenharmony_ci /* '%*[' not yet supported, invalid format */ 348162306a36Sopenharmony_ci if (*fmt == '[') 348262306a36Sopenharmony_ci return num; 348362306a36Sopenharmony_ci fmt++; 348462306a36Sopenharmony_ci } 348562306a36Sopenharmony_ci while (!isspace(*str) && *str) 348662306a36Sopenharmony_ci str++; 348762306a36Sopenharmony_ci continue; 348862306a36Sopenharmony_ci } 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_ci /* get field width */ 349162306a36Sopenharmony_ci field_width = -1; 349262306a36Sopenharmony_ci if (isdigit(*fmt)) { 349362306a36Sopenharmony_ci field_width = skip_atoi(&fmt); 349462306a36Sopenharmony_ci if (field_width <= 0) 349562306a36Sopenharmony_ci break; 349662306a36Sopenharmony_ci } 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci /* get conversion qualifier */ 349962306a36Sopenharmony_ci qualifier = -1; 350062306a36Sopenharmony_ci if (*fmt == 'h' || _tolower(*fmt) == 'l' || 350162306a36Sopenharmony_ci *fmt == 'z') { 350262306a36Sopenharmony_ci qualifier = *fmt++; 350362306a36Sopenharmony_ci if (unlikely(qualifier == *fmt)) { 350462306a36Sopenharmony_ci if (qualifier == 'h') { 350562306a36Sopenharmony_ci qualifier = 'H'; 350662306a36Sopenharmony_ci fmt++; 350762306a36Sopenharmony_ci } else if (qualifier == 'l') { 350862306a36Sopenharmony_ci qualifier = 'L'; 350962306a36Sopenharmony_ci fmt++; 351062306a36Sopenharmony_ci } 351162306a36Sopenharmony_ci } 351262306a36Sopenharmony_ci } 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_ci if (!*fmt) 351562306a36Sopenharmony_ci break; 351662306a36Sopenharmony_ci 351762306a36Sopenharmony_ci if (*fmt == 'n') { 351862306a36Sopenharmony_ci /* return number of characters read so far */ 351962306a36Sopenharmony_ci *va_arg(args, int *) = str - buf; 352062306a36Sopenharmony_ci ++fmt; 352162306a36Sopenharmony_ci continue; 352262306a36Sopenharmony_ci } 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci if (!*str) 352562306a36Sopenharmony_ci break; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci base = 10; 352862306a36Sopenharmony_ci is_sign = false; 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci switch (*fmt++) { 353162306a36Sopenharmony_ci case 'c': 353262306a36Sopenharmony_ci { 353362306a36Sopenharmony_ci char *s = (char *)va_arg(args, char*); 353462306a36Sopenharmony_ci if (field_width == -1) 353562306a36Sopenharmony_ci field_width = 1; 353662306a36Sopenharmony_ci do { 353762306a36Sopenharmony_ci *s++ = *str++; 353862306a36Sopenharmony_ci } while (--field_width > 0 && *str); 353962306a36Sopenharmony_ci num++; 354062306a36Sopenharmony_ci } 354162306a36Sopenharmony_ci continue; 354262306a36Sopenharmony_ci case 's': 354362306a36Sopenharmony_ci { 354462306a36Sopenharmony_ci char *s = (char *)va_arg(args, char *); 354562306a36Sopenharmony_ci if (field_width == -1) 354662306a36Sopenharmony_ci field_width = SHRT_MAX; 354762306a36Sopenharmony_ci /* first, skip leading white space in buffer */ 354862306a36Sopenharmony_ci str = skip_spaces(str); 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ci /* now copy until next white space */ 355162306a36Sopenharmony_ci while (*str && !isspace(*str) && field_width--) 355262306a36Sopenharmony_ci *s++ = *str++; 355362306a36Sopenharmony_ci *s = '\0'; 355462306a36Sopenharmony_ci num++; 355562306a36Sopenharmony_ci } 355662306a36Sopenharmony_ci continue; 355762306a36Sopenharmony_ci /* 355862306a36Sopenharmony_ci * Warning: This implementation of the '[' conversion specifier 355962306a36Sopenharmony_ci * deviates from its glibc counterpart in the following ways: 356062306a36Sopenharmony_ci * (1) It does NOT support ranges i.e. '-' is NOT a special 356162306a36Sopenharmony_ci * character 356262306a36Sopenharmony_ci * (2) It cannot match the closing bracket ']' itself 356362306a36Sopenharmony_ci * (3) A field width is required 356462306a36Sopenharmony_ci * (4) '%*[' (discard matching input) is currently not supported 356562306a36Sopenharmony_ci * 356662306a36Sopenharmony_ci * Example usage: 356762306a36Sopenharmony_ci * ret = sscanf("00:0a:95","%2[^:]:%2[^:]:%2[^:]", 356862306a36Sopenharmony_ci * buf1, buf2, buf3); 356962306a36Sopenharmony_ci * if (ret < 3) 357062306a36Sopenharmony_ci * // etc.. 357162306a36Sopenharmony_ci */ 357262306a36Sopenharmony_ci case '[': 357362306a36Sopenharmony_ci { 357462306a36Sopenharmony_ci char *s = (char *)va_arg(args, char *); 357562306a36Sopenharmony_ci DECLARE_BITMAP(set, 256) = {0}; 357662306a36Sopenharmony_ci unsigned int len = 0; 357762306a36Sopenharmony_ci bool negate = (*fmt == '^'); 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_ci /* field width is required */ 358062306a36Sopenharmony_ci if (field_width == -1) 358162306a36Sopenharmony_ci return num; 358262306a36Sopenharmony_ci 358362306a36Sopenharmony_ci if (negate) 358462306a36Sopenharmony_ci ++fmt; 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci for ( ; *fmt && *fmt != ']'; ++fmt, ++len) 358762306a36Sopenharmony_ci __set_bit((u8)*fmt, set); 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci /* no ']' or no character set found */ 359062306a36Sopenharmony_ci if (!*fmt || !len) 359162306a36Sopenharmony_ci return num; 359262306a36Sopenharmony_ci ++fmt; 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci if (negate) { 359562306a36Sopenharmony_ci bitmap_complement(set, set, 256); 359662306a36Sopenharmony_ci /* exclude null '\0' byte */ 359762306a36Sopenharmony_ci __clear_bit(0, set); 359862306a36Sopenharmony_ci } 359962306a36Sopenharmony_ci 360062306a36Sopenharmony_ci /* match must be non-empty */ 360162306a36Sopenharmony_ci if (!test_bit((u8)*str, set)) 360262306a36Sopenharmony_ci return num; 360362306a36Sopenharmony_ci 360462306a36Sopenharmony_ci while (test_bit((u8)*str, set) && field_width--) 360562306a36Sopenharmony_ci *s++ = *str++; 360662306a36Sopenharmony_ci *s = '\0'; 360762306a36Sopenharmony_ci ++num; 360862306a36Sopenharmony_ci } 360962306a36Sopenharmony_ci continue; 361062306a36Sopenharmony_ci case 'o': 361162306a36Sopenharmony_ci base = 8; 361262306a36Sopenharmony_ci break; 361362306a36Sopenharmony_ci case 'x': 361462306a36Sopenharmony_ci case 'X': 361562306a36Sopenharmony_ci base = 16; 361662306a36Sopenharmony_ci break; 361762306a36Sopenharmony_ci case 'i': 361862306a36Sopenharmony_ci base = 0; 361962306a36Sopenharmony_ci fallthrough; 362062306a36Sopenharmony_ci case 'd': 362162306a36Sopenharmony_ci is_sign = true; 362262306a36Sopenharmony_ci fallthrough; 362362306a36Sopenharmony_ci case 'u': 362462306a36Sopenharmony_ci break; 362562306a36Sopenharmony_ci case '%': 362662306a36Sopenharmony_ci /* looking for '%' in str */ 362762306a36Sopenharmony_ci if (*str++ != '%') 362862306a36Sopenharmony_ci return num; 362962306a36Sopenharmony_ci continue; 363062306a36Sopenharmony_ci default: 363162306a36Sopenharmony_ci /* invalid format; stop here */ 363262306a36Sopenharmony_ci return num; 363362306a36Sopenharmony_ci } 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci /* have some sort of integer conversion. 363662306a36Sopenharmony_ci * first, skip white space in buffer. 363762306a36Sopenharmony_ci */ 363862306a36Sopenharmony_ci str = skip_spaces(str); 363962306a36Sopenharmony_ci 364062306a36Sopenharmony_ci digit = *str; 364162306a36Sopenharmony_ci if (is_sign && digit == '-') { 364262306a36Sopenharmony_ci if (field_width == 1) 364362306a36Sopenharmony_ci break; 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci digit = *(str + 1); 364662306a36Sopenharmony_ci } 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci if (!digit 364962306a36Sopenharmony_ci || (base == 16 && !isxdigit(digit)) 365062306a36Sopenharmony_ci || (base == 10 && !isdigit(digit)) 365162306a36Sopenharmony_ci || (base == 8 && !isodigit(digit)) 365262306a36Sopenharmony_ci || (base == 0 && !isdigit(digit))) 365362306a36Sopenharmony_ci break; 365462306a36Sopenharmony_ci 365562306a36Sopenharmony_ci if (is_sign) 365662306a36Sopenharmony_ci val.s = simple_strntoll(str, 365762306a36Sopenharmony_ci field_width >= 0 ? field_width : INT_MAX, 365862306a36Sopenharmony_ci &next, base); 365962306a36Sopenharmony_ci else 366062306a36Sopenharmony_ci val.u = simple_strntoull(str, 366162306a36Sopenharmony_ci field_width >= 0 ? field_width : INT_MAX, 366262306a36Sopenharmony_ci &next, base); 366362306a36Sopenharmony_ci 366462306a36Sopenharmony_ci switch (qualifier) { 366562306a36Sopenharmony_ci case 'H': /* that's 'hh' in format */ 366662306a36Sopenharmony_ci if (is_sign) 366762306a36Sopenharmony_ci *va_arg(args, signed char *) = val.s; 366862306a36Sopenharmony_ci else 366962306a36Sopenharmony_ci *va_arg(args, unsigned char *) = val.u; 367062306a36Sopenharmony_ci break; 367162306a36Sopenharmony_ci case 'h': 367262306a36Sopenharmony_ci if (is_sign) 367362306a36Sopenharmony_ci *va_arg(args, short *) = val.s; 367462306a36Sopenharmony_ci else 367562306a36Sopenharmony_ci *va_arg(args, unsigned short *) = val.u; 367662306a36Sopenharmony_ci break; 367762306a36Sopenharmony_ci case 'l': 367862306a36Sopenharmony_ci if (is_sign) 367962306a36Sopenharmony_ci *va_arg(args, long *) = val.s; 368062306a36Sopenharmony_ci else 368162306a36Sopenharmony_ci *va_arg(args, unsigned long *) = val.u; 368262306a36Sopenharmony_ci break; 368362306a36Sopenharmony_ci case 'L': 368462306a36Sopenharmony_ci if (is_sign) 368562306a36Sopenharmony_ci *va_arg(args, long long *) = val.s; 368662306a36Sopenharmony_ci else 368762306a36Sopenharmony_ci *va_arg(args, unsigned long long *) = val.u; 368862306a36Sopenharmony_ci break; 368962306a36Sopenharmony_ci case 'z': 369062306a36Sopenharmony_ci *va_arg(args, size_t *) = val.u; 369162306a36Sopenharmony_ci break; 369262306a36Sopenharmony_ci default: 369362306a36Sopenharmony_ci if (is_sign) 369462306a36Sopenharmony_ci *va_arg(args, int *) = val.s; 369562306a36Sopenharmony_ci else 369662306a36Sopenharmony_ci *va_arg(args, unsigned int *) = val.u; 369762306a36Sopenharmony_ci break; 369862306a36Sopenharmony_ci } 369962306a36Sopenharmony_ci num++; 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci if (!next) 370262306a36Sopenharmony_ci break; 370362306a36Sopenharmony_ci str = next; 370462306a36Sopenharmony_ci } 370562306a36Sopenharmony_ci 370662306a36Sopenharmony_ci return num; 370762306a36Sopenharmony_ci} 370862306a36Sopenharmony_ciEXPORT_SYMBOL(vsscanf); 370962306a36Sopenharmony_ci 371062306a36Sopenharmony_ci/** 371162306a36Sopenharmony_ci * sscanf - Unformat a buffer into a list of arguments 371262306a36Sopenharmony_ci * @buf: input buffer 371362306a36Sopenharmony_ci * @fmt: formatting of buffer 371462306a36Sopenharmony_ci * @...: resulting arguments 371562306a36Sopenharmony_ci */ 371662306a36Sopenharmony_ciint sscanf(const char *buf, const char *fmt, ...) 371762306a36Sopenharmony_ci{ 371862306a36Sopenharmony_ci va_list args; 371962306a36Sopenharmony_ci int i; 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_ci va_start(args, fmt); 372262306a36Sopenharmony_ci i = vsscanf(buf, fmt, args); 372362306a36Sopenharmony_ci va_end(args); 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci return i; 372662306a36Sopenharmony_ci} 372762306a36Sopenharmony_ciEXPORT_SYMBOL(sscanf); 3728