mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-27 15:36:48 +00:00

Minimum version of binutils required to compile the kernel is 2.25. This version correctly handles the "rep" prefixes, so it is possible to remove the semicolon, which was used to support ancient versions of GNU as. Due to the semicolon, the compiler considers "rep; insn" (or its alternate "rep\n\tinsn" form) as two separate instructions. Removing the semicolon makes asm length calculations more accurate, consequently making scheduling and inlining decisions of the compiler more accurate. Removing the semicolon also enables assembler checks involving "rep" prefixes. Trying to assemble e.g. "rep addl %eax, %ebx" results in: Error: invalid instruction `add' after `rep' Signed-off-by: Uros Bizjak <ubizjak@gmail.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Cc: Andy Lutomirski <luto@kernel.org> Cc: Brian Gerst <brgerst@gmail.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Juergen Gross <jgross@suse.com> Cc: Kees Cook <keescook@chromium.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Martin Mares <mj@ucw.cz> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://lore.kernel.org/r/20250418071437.4144391-1-ubizjak@gmail.com
371 lines
8.4 KiB
C
371 lines
8.4 KiB
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" CC_SET(nz)
|
|
: CC_OUT(nz) (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);
|
|
}
|