Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
111 lines
3.3 KiB
C
111 lines
3.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
// SchOS — first-party userland runtime: _start entry and exit.
|
|
|
|
#include "core/types.h"
|
|
#include "uapi/user.h"
|
|
|
|
// The raw syscall. It takes the status in %rdi, and both callers below must
|
|
// pass one: this declaration used to be `void _xv6_exit(void)`, so the status
|
|
// was whatever the caller happened to leave in that register. Every program
|
|
// that ended with a bare exit() reported a random number, which is why
|
|
// `if socktest; then` could not work for any of them.
|
|
extern void _xv6_exit(int status);
|
|
|
|
// exit(void) in this library means exit(0). A program that wants to report
|
|
// something returns it from main -- lib/start.S passes that through now -- or
|
|
// calls exit_group(n).
|
|
void
|
|
exit(void)
|
|
{
|
|
_xv6_exit(0);
|
|
for (;;);
|
|
}
|
|
|
|
/* _start lives in lib/start.S — it must read incoming registers, which
|
|
* cannot be expressed portably in C. See that file. */
|
|
|
|
// The four the compiler is allowed to call without being asked.
|
|
//
|
|
// A freestanding build gets no libc, but it does not get a compiler that
|
|
// promises never to *call* one: the C standard lets an implementation emit
|
|
// calls to memcpy, memmove, memset and memcmp for things that look nothing
|
|
// like a function call. A struct assignment is the common one --
|
|
//
|
|
// *out = in; /* struct pciuser_info, 96 bytes */
|
|
//
|
|
// -- which gcc unrolled into stores and clang turned into `call memcpy`, so
|
|
// ldlsd linked for months and then did not, on a machine where nothing about
|
|
// it had changed except which compiler ran. The fix is not to avoid struct
|
|
// assignment; it is to provide the four functions, because the compiler is
|
|
// within its rights either way.
|
|
//
|
|
// Deliberately simple. These are not the hot path of anything here, and a
|
|
// clever one risks being turned back into a call to itself.
|
|
void *
|
|
memcpy(void *dst, const void *src, unsigned long n)
|
|
{
|
|
unsigned char *d = dst;
|
|
const unsigned char *s = src;
|
|
while (n--) *d++ = *s++;
|
|
return dst;
|
|
}
|
|
|
|
void *
|
|
memmove(void *dst, const void *src, unsigned long n)
|
|
{
|
|
unsigned char *d = dst;
|
|
const unsigned char *s = src;
|
|
|
|
if (d == s || n == 0)
|
|
return dst;
|
|
// Backwards when they overlap the wrong way, which is the whole reason
|
|
// this is not memcpy.
|
|
if (d < s) {
|
|
while (n--) *d++ = *s++;
|
|
} else {
|
|
d += n; s += n;
|
|
while (n--) *--d = *--s;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
void *
|
|
memset(void *dst, int c, unsigned long n)
|
|
{
|
|
unsigned char *d = dst;
|
|
while (n--) *d++ = (unsigned char)c;
|
|
return dst;
|
|
}
|
|
|
|
int
|
|
memcmp(const void *a, const void *b, unsigned long n)
|
|
{
|
|
const unsigned char *p = a, *q = b;
|
|
while (n--) {
|
|
if (*p != *q)
|
|
return *p - *q;
|
|
p++; q++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
__stack_chk_fail(void)
|
|
{
|
|
// A smashed stack is not a successful run. 134 is what a shell reports for
|
|
// SIGABRT, which is the nearest true thing this can say.
|
|
_xv6_exit(134);
|
|
for (;;);
|
|
}
|
|
|
|
// exec() without an environment.
|
|
//
|
|
// The syscall is execve, three arguments, because that is what Linux numbers
|
|
// 59 and what musl calls. The two-argument spelling stays because most of
|
|
// cmd/ has no environment to pass and should not have to say so -- but it
|
|
// says so *here*, explicitly, rather than leaving %rdx to chance.
|
|
int
|
|
exec(char *path, char **argv)
|
|
{
|
|
return execve(path, argv, 0);
|
|
}
|