Nexoniarz is the sole author and SchOS was never published under Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text, every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE, README.md, docs/licensing.md and the site say so. projects/schfs-linux follows the rest of the tree. The GPL gives copyleft: a fork has to ship its source. It also lifts the old "Linux drivers, never" rule, since GPL-2.0-only code combines with GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still has to pick one version, so docs/licensing.md now keeps Apache-2.0 and GPLv3 code out of the kernel to keep Linux code possible there. BSD 4-clause code goes from "avoid" to "no", because the advertising clause is GPL-incompatible. LDLS still ships no .ko. That boundary is now project policy rather than a legal requirement, and ldls.md, devices.md and licensing.md section 6 say so. refmod declares license=GPL, and ldls-check passes (27 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
145 lines
4 KiB
C
145 lines
4 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
//
|
|
// The string and memory primitives. See ldls/kpi/kpi.h for the provenance rule.
|
|
//
|
|
// These are the least interesting functions in the layer and the most
|
|
// dangerous to leave out, because a stub for one of them does not crash --
|
|
// it silently does nothing, and the caller carries on with a buffer full of
|
|
// whatever was there before.
|
|
//
|
|
// That is not hypothetical. `memcpy` was a logging stub while the receive path
|
|
// was being brought up: the driver copied each frame into its skb, the copy
|
|
// did not happen, and the stack was handed the right *length* of zeroes. Every
|
|
// counter said packets were arriving and DHCP never completed. A crash would
|
|
// have been easier to find.
|
|
|
|
#include "types.h"
|
|
#include "kpi.h"
|
|
|
|
void *
|
|
kpi_memcpy(void *d, const void *s, unsigned long n)
|
|
{
|
|
unsigned char *a = (unsigned char *)d;
|
|
const unsigned char *b = (const unsigned char *)s;
|
|
while (n--) *a++ = *b++;
|
|
return d;
|
|
}
|
|
|
|
void *
|
|
kpi_memmove(void *d, const void *s, unsigned long n)
|
|
{
|
|
unsigned char *a = (unsigned char *)d;
|
|
const unsigned char *b = (const unsigned char *)s;
|
|
|
|
if (a == b || n == 0)
|
|
return d;
|
|
if (a < b) {
|
|
while (n--) *a++ = *b++;
|
|
} else {
|
|
a += n; b += n;
|
|
while (n--) *--a = *--b;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
void *
|
|
kpi_memset(void *d, int c, unsigned long n)
|
|
{
|
|
unsigned char *a = (unsigned char *)d;
|
|
while (n--) *a++ = (unsigned char)c;
|
|
return d;
|
|
}
|
|
|
|
int
|
|
kpi_memcmp(const void *x, const void *y, unsigned long n)
|
|
{
|
|
const unsigned char *a = x, *b = y;
|
|
while (n--) { if (*a != *b) return (int)*a - (int)*b; a++; b++; }
|
|
return 0;
|
|
}
|
|
|
|
// A null string is survivable, and reported.
|
|
//
|
|
// A hosted driver reads names out of structures this host only partly fills
|
|
// in, and the first one it found was `pci_name(pdev)` -- a pointer into a
|
|
// `struct device` that had never been given one. Faulting inside strnlen tells
|
|
// you nothing about which field was missing; answering zero and saying so
|
|
// leaves the driver running and names the problem.
|
|
static long kpi_nullstr;
|
|
|
|
static int
|
|
str_ok(const char *s, const char *who)
|
|
{
|
|
if (s)
|
|
return 1;
|
|
if (++kpi_nullstr <= 4) {
|
|
kpi_log(" kpi: "); kpi_log(who);
|
|
kpi_log(" on a null string -- some field of a struct this host fills in "
|
|
"only partly\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned long
|
|
kpi_strlen(const char *s)
|
|
{
|
|
unsigned long n = 0;
|
|
if (!str_ok(s, "strlen")) return 0;
|
|
while (s[n]) n++;
|
|
return n;
|
|
}
|
|
|
|
unsigned long
|
|
kpi_strnlen(const char *s, unsigned long max)
|
|
{
|
|
unsigned long n = 0;
|
|
if (!str_ok(s, "strnlen")) return 0;
|
|
while (n < max && s[n]) n++;
|
|
return n;
|
|
}
|
|
|
|
// sized_strscpy(dest, src, count) -- a bounded copy that always terminates and
|
|
// reports the length copied, or -E2BIG when it did not fit.
|
|
long
|
|
kpi_sized_strscpy(char *d, const char *s, unsigned long n)
|
|
{
|
|
unsigned long i = 0;
|
|
|
|
if (n == 0)
|
|
return -7; /* -E2BIG */
|
|
if (!str_ok(s, "strscpy")) { d[0] = 0; return 0; }
|
|
while (i + 1 < n && s[i]) { d[i] = s[i]; i++; }
|
|
d[i] = 0;
|
|
return s[i] ? -7 : (long)i;
|
|
}
|
|
|
|
// The io variants. On this platform a BAR is ordinary memory once mapped, so
|
|
// these differ from their plain counterparts only in that the compiler must
|
|
// not reorder or elide them -- which is what the volatile is for.
|
|
void
|
|
kpi_memcpy_fromio(void *d, const void *src, unsigned long n)
|
|
{
|
|
unsigned char *a = (unsigned char *)d;
|
|
const volatile unsigned char *b = (const volatile unsigned char *)src;
|
|
while (n--) *a++ = *b++;
|
|
}
|
|
|
|
void
|
|
kpi_memcpy_toio(void *dst, const void *s, unsigned long n)
|
|
{
|
|
volatile unsigned char *a = (volatile unsigned char *)dst;
|
|
const unsigned char *b = (const unsigned char *)s;
|
|
while (n--) *a++ = *b++;
|
|
}
|
|
|
|
// __fortify_panic is what a driver's bounds-checked string helpers call when
|
|
// they detect an overflow. Reaching it means the driver found a bug in itself,
|
|
// and the honest thing is to say so rather than return.
|
|
void
|
|
kpi_fortify_panic(void)
|
|
{
|
|
kpi_log("kpi: the driver called __fortify_panic -- it detected an "
|
|
"overflow in itself\n");
|
|
for (;;)
|
|
;
|
|
}
|