root/arch/x86/boot/string.c
// SPDX-License-Identifier: GPL-2.0-only
/* -*- linux-c -*- ------------------------------------------------------- *
 *
 *   Copyright (C) 1991, 1992 Linus Torvalds
 *   Copyright 2007 rPath, Inc. - All Rights Reserved
 *
 * ----------------------------------------------------------------------- */

/*
 * Very basic string functions
 */

#include <linux/types.h>
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/limits.h>
#include <asm/asm.h>
#include "ctype.h"
#include "string.h"

#define KSTRTOX_OVERFLOW       (1U << 31)

/*
 * Undef these macros so that the functions that we provide
 * here will have the correct names regardless of how string.h
 * may have chosen to #define them.
 */
#undef memcpy
#undef memset
#undef memcmp

int memcmp(const void *s1, const void *s2, size_t len)
{
        bool diff;
        asm("repe cmpsb"
            : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len));
        return diff;
}

/*
 * Clang may lower `memcmp == 0` to `bcmp == 0`.
 */
int bcmp(const void *s1, const void *s2, size_t len)
{
        return memcmp(s1, s2, len);
}

int strcmp(const char *str1, const char *str2)
{
        const unsigned char *s1 = (const unsigned char *)str1;
        const unsigned char *s2 = (const unsigned char *)str2;
        int delta;

        while (*s1 || *s2) {
                delta = *s1 - *s2;
                if (delta)
                        return delta;
                s1++;
                s2++;
        }
        return 0;
}

int strncmp(const char *cs, const char *ct, size_t count)
{
        unsigned char c1, c2;

        while (count) {
                c1 = *cs++;
                c2 = *ct++;
                if (c1 != c2)
                        return c1 < c2 ? -1 : 1;
                if (!c1)
                        break;
                count--;
        }
        return 0;
}

size_t strnlen(const char *s, size_t maxlen)
{
        const char *es = s;
        while (*es && maxlen) {
                es++;
                maxlen--;
        }

        return (es - s);
}

/* Works only for digits and letters, but small and fast */
#define TOLOWER(x) ((x) | 0x20)

static unsigned int simple_guess_base(const char *cp)
{
        if (cp[0] == '0') {
                if (TOLOWER(cp[1]) == 'x' && isxdigit(cp[2]))
                        return 16;
                else
                        return 8;
        } else {
                return 10;
        }
}

/**
 * simple_strtoull - convert a string to an unsigned long long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
{
        unsigned long long result = 0;

        if (!base)
                base = simple_guess_base(cp);

        if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x')
                cp += 2;

        while (isxdigit(*cp)) {
                unsigned int value;

                value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10;
                if (value >= base)
                        break;
                result = result * base + value;
                cp++;
        }
        if (endp)
                *endp = (char *)cp;

        return result;
}

long simple_strtol(const char *cp, char **endp, unsigned int base)
{
        if (*cp == '-')
                return -simple_strtoull(cp + 1, endp, base);

        return simple_strtoull(cp, endp, base);
}

/**
 * strlen - Find the length of a string
 * @s: The string to be sized
 */
size_t strlen(const char *s)
{
        const char *sc;

        for (sc = s; *sc != '\0'; ++sc)
                /* nothing */;
        return sc - s;
}

/**
 * strstr - Find the first substring in a %NUL terminated string
 * @s1: The string to be searched
 * @s2: The string to search for
 */
char *strstr(const char *s1, const char *s2)
{
        size_t l1, l2;

        l2 = strlen(s2);
        if (!l2)
                return (char *)s1;
        l1 = strlen(s1);
        while (l1 >= l2) {
                l1--;
                if (!memcmp(s1, s2, l2))
                        return (char *)s1;
                s1++;
        }
        return NULL;
}

/**
 * strchr - Find the first occurrence of the character c in the string s.
 * @s: the string to be searched
 * @c: the character to search for
 */
char *strchr(const char *s, int c)
{
        while (*s != (char)c)
                if (*s++ == '\0')
                        return NULL;
        return (char *)s;
}

static inline u64 __div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
{
        union {
                u64 v64;
                u32 v32[2];
        } d = { dividend };
        u32 upper;

        upper = d.v32[1];
        d.v32[1] = 0;
        if (upper >= divisor) {
                d.v32[1] = upper / divisor;
                upper %= divisor;
        }
        asm ("divl %2" : "=a" (d.v32[0]), "=d" (*remainder) :
                "rm" (divisor), "0" (d.v32[0]), "1" (upper));
        return d.v64;
}

static inline u64 __div_u64(u64 dividend, u32 divisor)
{
        u32 remainder;

        return __div_u64_rem(dividend, divisor, &remainder);
}

static inline char _tolower(const char c)
{
        return c | 0x20;
}

static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
{
        if (*base == 0) {
                if (s[0] == '0') {
                        if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
                                *base = 16;
                        else
                                *base = 8;
                } else
                        *base = 10;
        }
        if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
                s += 2;
        return s;
}

/*
 * Convert non-negative integer string representation in explicitly given radix
 * to an integer.
 * Return number of characters consumed maybe or-ed with overflow bit.
 * If overflow occurs, result integer (incorrect) is still returned.
 *
 * Don't you dare use this function.
 */
static unsigned int _parse_integer(const char *s,
                                   unsigned int base,
                                   unsigned long long *p)
{
        unsigned long long res;
        unsigned int rv;

        res = 0;
        rv = 0;
        while (1) {
                unsigned int c = *s;
                unsigned int lc = c | 0x20; /* don't tolower() this line */
                unsigned int val;

                if ('0' <= c && c <= '9')
                        val = c - '0';
                else if ('a' <= lc && lc <= 'f')
                        val = lc - 'a' + 10;
                else
                        break;

                if (val >= base)
                        break;
                /*
                 * Check for overflow only if we are within range of
                 * it in the max base we support (16)
                 */
                if (unlikely(res & (~0ull << 60))) {
                        if (res > __div_u64(ULLONG_MAX - val, base))
                                rv |= KSTRTOX_OVERFLOW;
                }
                res = res * base + val;
                rv++;
                s++;
        }
        *p = res;
        return rv;
}

static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
        unsigned long long _res;
        unsigned int rv;

        s = _parse_integer_fixup_radix(s, &base);
        rv = _parse_integer(s, base, &_res);
        if (rv & KSTRTOX_OVERFLOW)
                return -ERANGE;
        if (rv == 0)
                return -EINVAL;
        s += rv;
        if (*s == '\n')
                s++;
        if (*s)
                return -EINVAL;
        *res = _res;
        return 0;
}

/**
 * kstrtoull - convert a string to an unsigned long long
 * @s: The start of the string. The string must be null-terminated, and may also
 *  include a single newline before its terminating null. The first character
 *  may also be a plus sign, but not a minus sign.
 * @base: The number base to use. The maximum supported base is 16. If base is
 *  given as 0, then the base of the string is automatically detected with the
 *  conventional semantics - If it begins with 0x the number will be parsed as a
 *  hexadecimal (case insensitive), if it otherwise begins with 0, it will be
 *  parsed as an octal number. Otherwise it will be parsed as a decimal.
 * @res: Where to write the result of the conversion on success.
 *
 * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error.
 * Used as a replacement for the obsolete simple_strtoull. Return code must
 * be checked.
 */
int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
        if (s[0] == '+')
                s++;
        return _kstrtoull(s, base, res);
}

static int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
{
        unsigned long long tmp;
        int rv;

        rv = kstrtoull(s, base, &tmp);
        if (rv < 0)
                return rv;
        if (tmp != (unsigned long)tmp)
                return -ERANGE;
        *res = tmp;
        return 0;
}

/**
 * boot_kstrtoul - convert a string to an unsigned long
 * @s: The start of the string. The string must be null-terminated, and may also
 *  include a single newline before its terminating null. The first character
 *  may also be a plus sign, but not a minus sign.
 * @base: The number base to use. The maximum supported base is 16. If base is
 *  given as 0, then the base of the string is automatically detected with the
 *  conventional semantics - If it begins with 0x the number will be parsed as a
 *  hexadecimal (case insensitive), if it otherwise begins with 0, it will be
 *  parsed as an octal number. Otherwise it will be parsed as a decimal.
 * @res: Where to write the result of the conversion on success.
 *
 * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error.
 * Used as a replacement for the simple_strtoull.
 */
int boot_kstrtoul(const char *s, unsigned int base, unsigned long *res)
{
        /*
         * We want to shortcut function call, but
         * __builtin_types_compatible_p(unsigned long, unsigned long long) = 0.
         */
        if (sizeof(unsigned long) == sizeof(unsigned long long) &&
            __alignof__(unsigned long) == __alignof__(unsigned long long))
                return kstrtoull(s, base, (unsigned long long *)res);
        else
                return _kstrtoul(s, base, res);
}