Initial commit: Schunix as it stands before the SchOS rename

An x86-64 operating system built from xv6, now largely rewritten: SMP,
a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF
rasteriser, and a Linux-numbered syscall ABI.

No history before this point -- the tree was never under version control.
This commit exists so the rename that follows has something to undo to.
This commit is contained in:
Nexoniarz 2026-08-09 13:47:01 +02:00
commit d0f7acf9eb
275 changed files with 51417 additions and 0 deletions

219
cmd/fbinfo/main.c Normal file
View file

@ -0,0 +1,219 @@
// fbinfo -- ask /dev/fb0 what it is, and optionally draw on it.
//
// This exists because the framebuffer is the one device whose correctness you
// cannot check by reading kernel source: the numbers have to come back from
// the hardware the bootloader actually programmed. Three places in the kernel
// used to assume the framebuffer lived at 0xfd000000 and was 800x600x32; they
// agreed with QEMU's stdvga by luck, and nothing would have told us when the
// luck ran out. Now `fbinfo` would.
//
// fbinfo print the geometry
// fbinfo fill draw a gradient over the whole screen through mmap
// fbinfo bars draw colour bars through write(), no mmap involved
//
// The two drawing modes take different paths on purpose. If `fill` works and
// `bars` does not, the mapping is right and the device's read/write is wrong;
// if it is the other way round, the reverse. Having both makes a failure say
// which half broke.
#include "types.h"
#include "user.h"
#include "fcntl.h"
#include "fb.h"
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define MAP_SHARED 0x01
static void
puts(const char *s)
{
int n = 0;
while (s[n]) n++;
write(1, (void*)s, n);
}
// Unsigned decimal. No printf in lib/, and pulling one in for six numbers
// would be a worse trade than fourteen lines.
static void
putu(unsigned long v)
{
char buf[24];
int i = 0;
if (v == 0) {
write(1, "0", 1);
return;
}
while (v > 0 && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i > 0) {
i--;
write(1, &buf[i], 1);
}
}
static void
puthex(unsigned long v)
{
static const char d[] = "0123456789abcdef";
char buf[16];
int i = 0;
write(1, "0x", 2);
if (v == 0) {
write(1, "0", 1);
return;
}
while (v > 0 && i < (int)sizeof(buf)) {
buf[i++] = d[v & 0xf];
v >>= 4;
}
while (i > 0) {
i--;
write(1, &buf[i], 1);
}
}
// Name, then spaces out to a fixed column. Padding inside each string
// literal is what produced "bits_per_pixel32": the longest name happened to
// be exactly as wide as the column, so it got no separator at all and the
// value ran straight into it.
static void
label(const char *name)
{
int n = 0;
while (name[n]) n++;
write(1, (void*)name, n);
while (n < 16) { write(1, " ", 1); n++; }
}
static void
field(const char *name, unsigned long v)
{
label(name);
putu(v);
puts("\n");
}
static void
chanline(const char *name, struct fb_bitfield *b)
{
label(name);
puts("offset ");
putu(b->offset);
puts(", length ");
putu(b->length);
if (b->length == 0)
puts(" (absent)");
puts("\n");
}
int
main(int argc, char *argv[])
{
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
int fd;
fd = open("/dev/fb0", O_RDWR);
if (fd < 0) {
puts("fbinfo: cannot open /dev/fb0\n");
return 1;
}
if (ioctl(fd, FBIOGET_VSCREENINFO, &var) < 0) {
puts("fbinfo: FBIOGET_VSCREENINFO failed\n");
return 1;
}
if (ioctl(fd, FBIOGET_FSCREENINFO, &fix) < 0) {
puts("fbinfo: FBIOGET_FSCREENINFO failed\n");
return 1;
}
if (argc < 2) {
label("id");
puts(fix.id);
puts("\n");
field("xres", var.xres);
field("yres", var.yres);
field("bits_per_pixel", var.bits_per_pixel);
label("smem_start");
puthex(fix.smem_start);
puts("\n");
field("smem_len", fix.smem_len);
field("line_length", fix.line_length);
// The stride is the number worth staring at. A card is free to pad
// each row, and a program that computes row starts as
// y * xres * bytes_per_pixel instead of y * line_length draws a
// picture that shears progressively down the screen.
if (fix.line_length != var.xres * (var.bits_per_pixel / 8)) {
label("note");
puts("stride is padded; rows are not xres*bpp/8 apart\n");
}
chanline("red", &var.red);
chanline("green", &var.green);
chanline("blue", &var.blue);
chanline("transp", &var.transp);
return 0;
}
if (argv[1][0] == 'f') { // fill
unsigned char *fb = mmap(0, fix.smem_len,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (fb == 0 || (long)fb < 0) {
puts("fbinfo: mmap failed\n");
return 1;
}
unsigned bpp = var.bits_per_pixel / 8;
for (unsigned y = 0; y < var.yres; y++) {
unsigned char *row = fb + (unsigned long)y * fix.line_length;
for (unsigned x = 0; x < var.xres; x++) {
unsigned r = (x * 255) / (var.xres ? var.xres : 1);
unsigned g = (y * 255) / (var.yres ? var.yres : 1);
unsigned long px = ((unsigned long)r << var.red.offset) |
((unsigned long)g << var.green.offset) |
((unsigned long)0x80 << var.blue.offset);
unsigned char *p = row + (unsigned long)x * bpp;
for (unsigned b = 0; b < bpp; b++)
p[b] = (unsigned char)(px >> (b * 8));
}
}
puts("fbinfo: gradient drawn through mmap\n");
return 0;
}
if (argv[1][0] == 'b') { // bars
// One scanline at a time through write(). No mapping: this is the
// path that used to be devsw's nullwrite, i.e. a silent no-op.
static unsigned char row[8192];
unsigned bpp = var.bits_per_pixel / 8;
unsigned len = fix.line_length;
if (len > sizeof(row))
len = sizeof(row);
for (unsigned y = 0; y < var.yres; y++) {
unsigned band = (y * 8) / (var.yres ? var.yres : 1);
unsigned long px =
((unsigned long)((band & 1) ? 255 : 0) << var.red.offset) |
((unsigned long)((band & 2) ? 255 : 0) << var.green.offset) |
((unsigned long)((band & 4) ? 255 : 0) << var.blue.offset);
for (unsigned x = 0; x * bpp + bpp <= len; x++)
for (unsigned b = 0; b < bpp; b++)
row[x * bpp + b] = (unsigned char)(px >> (b * 8));
if (write(fd, row, len) != (int)len) {
puts("fbinfo: short write to /dev/fb0\n");
return 1;
}
}
puts("fbinfo: colour bars drawn through write()\n");
return 0;
}
puts("usage: fbinfo [fill|bars]\n");
return 1;
}