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>
139 lines
4 KiB
C
139 lines
4 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
//
|
|
// schdbg -- ask the kernel a question.
|
|
//
|
|
// docs/architecture/debugging.md, milestone D2. The kernel half is /dev/dbg: write a
|
|
// line, read the answer. This is the half that makes that pleasant, and it is
|
|
// deliberately almost nothing -- because the protocol is lines of text, and a
|
|
// protocol whose client has to be clever is a protocol you cannot use when the
|
|
// client will not start.
|
|
//
|
|
// schdbg the views this kernel publishes
|
|
// schdbg dma one of them
|
|
// schdbg all all of them
|
|
// schdbg -i a prompt, for when you are asking several
|
|
//
|
|
// Everything here works equally well as:
|
|
//
|
|
// echo dma > /dev/dbg && cat /dev/dbg
|
|
//
|
|
// which is the point, and is what you fall back to on a system where this
|
|
// program is one of the things that is broken.
|
|
|
|
#include "types.h"
|
|
#include "user.h"
|
|
#include "fcntl.h"
|
|
|
|
static void
|
|
puts_(const char *s)
|
|
{
|
|
int n = 0;
|
|
while (s[n]) n++;
|
|
write(1, (void *)s, n);
|
|
}
|
|
|
|
// One question, one answer.
|
|
//
|
|
// The descriptor is opened fresh for each, because the answer is read from
|
|
// offset zero and the kernel replaces it on every write. Reusing one open
|
|
// descriptor across two questions would read the second answer from wherever
|
|
// the first one left off.
|
|
static int
|
|
ask(const char *cmd)
|
|
{
|
|
char buf[512];
|
|
int fd, n;
|
|
|
|
fd = open("/dev/dbg", O_RDWR);
|
|
if (fd < 0) {
|
|
puts_("schdbg: cannot open /dev/dbg\n");
|
|
return -1;
|
|
}
|
|
|
|
if (cmd) {
|
|
int len = 0;
|
|
while (cmd[len]) len++;
|
|
if (write(fd, (void *)cmd, len) != len) {
|
|
puts_("schdbg: the kernel would not take that command\n");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
// Back to the start. Writing moved this descriptor's offset forward by
|
|
// the length of the command, and the answer begins at zero -- so
|
|
// without this the reply arrives with its first few characters missing,
|
|
// which is subtle enough to look like a formatting bug in the view.
|
|
lseek(fd, 0, 0); /* SEEK_SET */
|
|
}
|
|
|
|
while ((n = read(fd, buf, sizeof(buf))) > 0)
|
|
write(1, buf, n);
|
|
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
// A line from the terminal, without the newline. Returns 0 at end of input.
|
|
static int
|
|
readline(char *buf, int cap)
|
|
{
|
|
int n = 0;
|
|
char c;
|
|
|
|
for (;;) {
|
|
int r = read(0, &c, 1);
|
|
if (r <= 0)
|
|
return n > 0;
|
|
if (c == '\n') {
|
|
buf[n] = 0;
|
|
return 1;
|
|
}
|
|
if (n < cap - 1)
|
|
buf[n++] = c;
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'i') {
|
|
char line[128];
|
|
|
|
puts_("schdbg: ask the kernel something. 'help' lists the views, "
|
|
"'quit' leaves.\n");
|
|
for (;;) {
|
|
puts_("dbg> ");
|
|
if (!readline(line, sizeof(line)))
|
|
break;
|
|
if (line[0] == 0)
|
|
continue;
|
|
if (line[0] == 'q' && line[1] == 'u' && line[2] == 'i' &&
|
|
line[3] == 't' && line[4] == 0)
|
|
break;
|
|
ask(line);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// No argument means "what can I ask", which is the question someone has
|
|
// the first time and the one an empty response would not answer.
|
|
if (argc < 2)
|
|
return ask("help") < 0 ? 1 : 0;
|
|
|
|
// Everything after the program name, rejoined with single spaces. The
|
|
// shell split it and the kernel wants a line; `schdbg vma 4` has to arrive
|
|
// as "vma 4" or the argument is silently dropped and the view quietly
|
|
// ignores the narrowing it was asked for.
|
|
{
|
|
char line[128];
|
|
int n = 0;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
if (i > 1 && n < (int)sizeof(line) - 1)
|
|
line[n++] = ' ';
|
|
for (int j = 0; argv[i][j] && n < (int)sizeof(line) - 1; j++)
|
|
line[n++] = argv[i][j];
|
|
}
|
|
line[n] = 0;
|
|
return ask(line) < 0 ? 1 : 0;
|
|
}
|
|
}
|