The cursor now costs nothing for a byte inside an escape sequence and one cell fewer for a plain glyph: the grid is the truth, so the old cursor cell is repainted after the byte, and not at all when a glyph has just been drawn over it. __fortify_panic says "r8169: rtl_open.cold+0x..." through ldlsd's module list, so a photograph of the screen is enough, and exits instead of spinning on a core and holding /dev/netdev. docs/plan/finch.md: only integrating Finch is later; finishing it is not. The self-hosting gap measured against Finch's own bootstrap test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015f73FesGZGYPkmDvUcj6QX
166 lines
5 KiB
C
166 lines
5 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 "core/types.h"
|
|
#include "uapi/user.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_where)(unsigned long addr);
|
|
|
|
void
|
|
kpi_fortify_panic(unsigned int reason, unsigned long avail, unsigned long size)
|
|
{
|
|
kpi_log("kpi: the driver called __fortify_panic -- it detected an "
|
|
"overflow in itself\n");
|
|
// Which check, how much it thought it had, how much it wanted, and where
|
|
// from -- the address is in the driver's own text, so subtract the load
|
|
// address logged for it and look the offset up with nm. This took a
|
|
// hardware round trip to find the last time it said nothing.
|
|
kpi_log(" reason "); kpi_lognum(reason);
|
|
kpi_log(", available "); kpi_lognum((long long)avail);
|
|
kpi_log(", wanted "); kpi_lognum((long long)size);
|
|
kpi_log(", from ");
|
|
if (kpi_where)
|
|
kpi_where((unsigned long)__builtin_return_address(0));
|
|
else {
|
|
kpi_log("0x"); kpi_loghex((unsigned long)__builtin_return_address(0), 16);
|
|
}
|
|
kpi_log("\n");
|
|
// Not spin: a driver that has found a bug in itself holds /dev/netdev and
|
|
// one core at 100% until reboot. The job is KeepAlive = no, so leaving is
|
|
// free, and the kernel takes the interface back.
|
|
exit_group(1);
|
|
for (;;)
|
|
;
|
|
}
|