Cheaper cursor, a fortify panic that names its function; Finch's gap
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
This commit is contained in:
parent
2651ff2169
commit
9c729edac1
5 changed files with 101 additions and 15 deletions
|
|
@ -326,6 +326,33 @@ find_symbol(struct ko *k, const char *want)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "r8169: rtl_open.cold+0x4f" for an address in a loaded module, so that a
|
||||||
|
// photograph of the screen is enough to find the line. Modules are named by
|
||||||
|
// the file they were loaded from, with the directory and ".ko" dropped.
|
||||||
|
static const char *g_modname[MAXMOD];
|
||||||
|
|
||||||
|
static void
|
||||||
|
where(unsigned long addr)
|
||||||
|
{
|
||||||
|
for (int m = 0; m < g_nmods; m++) {
|
||||||
|
struct ko *k = g_mods[m];
|
||||||
|
for (int i = 0; i < k->nsym; i++) {
|
||||||
|
const struct ko_sym *s = &k->sym[i];
|
||||||
|
if (s->st_shndx == KO_SHN_UNDEF || s->st_shndx >= (unsigned short)k->nsh ||
|
||||||
|
!k->sec[s->st_shndx].alloc || s->st_size == 0 || (s->st_info & 0xf) != 2)
|
||||||
|
continue;
|
||||||
|
unsigned long long a = k->sec[s->st_shndx].addr + s->st_value;
|
||||||
|
if (addr >= a && addr < a + s->st_size) {
|
||||||
|
puts_(g_modname[m]); puts_(": ");
|
||||||
|
puts_(k->str + s->st_name); puts_("+");
|
||||||
|
puthex(addr - a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
puthex(addr);
|
||||||
|
}
|
||||||
|
|
||||||
// --- reading and writing a struct at an offset BTF gave us --------------------
|
// --- reading and writing a struct at an offset BTF gave us --------------------
|
||||||
|
|
||||||
static unsigned int
|
static unsigned int
|
||||||
|
|
@ -1009,8 +1036,14 @@ load_module(const char *path)
|
||||||
puts_("\n");
|
puts_("\n");
|
||||||
|
|
||||||
entry = find_symbol(k, "init_module");
|
entry = find_symbol(k, "init_module");
|
||||||
if (g_nmods < MAXMOD)
|
if (g_nmods < MAXMOD) {
|
||||||
|
const char *b = path, *q;
|
||||||
|
for (q = path; *q; q++)
|
||||||
|
if (*q == '/') b = q + 1;
|
||||||
|
g_modname[g_nmods] = b;
|
||||||
g_mods[g_nmods++] = k;
|
g_mods[g_nmods++] = k;
|
||||||
|
kpi_where = where;
|
||||||
|
}
|
||||||
if (!entry) { puts_(" no init_module -- nothing to call\n"); return; }
|
if (!entry) { puts_(" no init_module -- nothing to call\n"); return; }
|
||||||
|
|
||||||
puts_("\n calling init_module\n\n");
|
puts_("\n calling init_module\n\n");
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
# Finch, for SchOS's own programs
|
# Finch, for SchOS's own programs
|
||||||
|
|
||||||
**Later. Not being worked on** — it is behind [1.0.md](1.0.md)'s freeze like
|
**Two things, and only one of them is later.**
|
||||||
everything else here that is not named there. This records the decision so it
|
|
||||||
is not rediscovered.
|
- *Bringing Finch into SchOS* (below) is **later** — behind [1.0.md](1.0.md)'s
|
||||||
|
freeze like everything else here that is not named there.
|
||||||
|
- *Finishing Finch itself*, and making it fully self-hosting, is work on the
|
||||||
|
Finch repository and does not touch this tree. It can proceed now; see
|
||||||
|
[the gap](#the-self-hosting-gap).
|
||||||
|
|
||||||
Finch is the author's own language: <https://pandor.pl/git/nexoniarz/Finch>
|
Finch is the author's own language: <https://pandor.pl/git/nexoniarz/Finch>
|
||||||
(GitHub mirror: `Nexoniarz/Finch`). The plan is to write SchOS's own
|
(GitHub mirror: `Nexoniarz/Finch`). The plan is to write SchOS's own
|
||||||
|
|
@ -31,7 +35,7 @@ Measured from the repository, not from its README alone:
|
||||||
- **Tooling:** a language server (`finch lsp`), VS Code and Kate support, 74
|
- **Tooling:** a language server (`finch lsp`), VS Code and Kate support, 74
|
||||||
tests.
|
tests.
|
||||||
- **Self-hosting:** `boot/` is a Finch compiler written in Finch (about 3,100
|
- **Self-hosting:** `boot/` is a Finch compiler written in Finch (about 3,100
|
||||||
lines, against about 5,000 for the C++ one in `src/`) that builds itself to a
|
lines, against about 5,900 for the C++ one in `src/`, language server included) that builds itself to a
|
||||||
byte-identical result — `tests/boot.sh`.
|
byte-identical result — `tests/boot.sh`.
|
||||||
|
|
||||||
## What SchOS would need to do
|
## What SchOS would need to do
|
||||||
|
|
@ -53,16 +57,37 @@ Measured from the repository, not from its README alone:
|
||||||
the headers in `include/uapi/`, so they are not hand-written and cannot
|
the headers in `include/uapi/`, so they are not hand-written and cannot
|
||||||
drift from the kernel.
|
drift from the kernel.
|
||||||
|
|
||||||
## What "finishing Finch" means is not settled
|
## The self-hosting gap
|
||||||
|
|
||||||
The repository has no list of what is unfinished. Before work starts, the
|
"Rewrite it in Finch itself" is mostly done: `boot/` compiles itself. What it
|
||||||
author names it. Candidates seen in the tree: `note.txt` and `notatka.txt` are
|
does **not** yet do is stated by its own test, `tests/boot.sh`, which skips
|
||||||
scratch files; `OpenGL.fn` was deleted in the last commit; the `boot/` compiler
|
every test that uses a feature `boot/` lacks. Counting mentions in the two
|
||||||
is shorter than the C++ one, so some language features may exist only in the
|
sources confirms it (`boot/` vs `src/`):
|
||||||
latter. **The first task is to diff the two feature sets**, not to guess.
|
|
||||||
|
| Feature | `boot/` | `src/` | Needed for |
|
||||||
|
|---|---|---|---|
|
||||||
|
| maps | 0 | 185 | `codegen_map.cpp` (242 lines) to port |
|
||||||
|
| errors as values (`or`, `try`) | few | hundreds | most real programs |
|
||||||
|
| methods on user types | 17 | 87 | partial |
|
||||||
|
| `defer` | 2 | 31 | partial |
|
||||||
|
| sized numbers (`u8`..`u64`, `i8`..`i32`, `f32`, `f64`) | none | throughout | kernel-facing code |
|
||||||
|
| enums | 0 | 14 | |
|
||||||
|
| C import (`import "x.h"`, `link`) | none | `cimport.cpp`, 373 lines on libclang | **the way into every C library, SchOS's included** |
|
||||||
|
| target and ABI layer (AArch64, macOS, Windows) | 0 | `target.cpp`, `abi.cpp` | non-Linux-x86 targets |
|
||||||
|
| language server | 0 | `lsp.cpp`, 580 lines | editors |
|
||||||
|
|
||||||
|
So "finishing" has a concrete order: **errors as values and maps first** (they
|
||||||
|
are what the rest uses), then sized numbers and `defer`, then C import — which
|
||||||
|
needs libclang bindings, so it needs C import's own simpler form first — and
|
||||||
|
the target layer last. `tests/boot.sh` is the measure: the list of skipped
|
||||||
|
tests shrinks to nothing.
|
||||||
|
|
||||||
|
The scratch files `note.txt` and `notatka.txt` in the repository are not
|
||||||
|
Finch's and can go.
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
- Nothing in the kernel is rewritten in Finch. It is for applications and tools.
|
- Inference, not an instruction: the kernel stays in C. Finch is for
|
||||||
|
applications and tools; the user said "our apps or stuff like that".
|
||||||
- No new capability starts until [the register of unfinished work](1.0.md#the-freeze-and-the-register-of-unfinished-work)
|
- No new capability starts until [the register of unfinished work](1.0.md#the-freeze-and-the-register-of-unfinished-work)
|
||||||
is empty; this document is a plan, not an exception to that.
|
is empty; the SchOS half of this document is a plan, not an exception to that.
|
||||||
|
|
|
||||||
|
|
@ -1241,7 +1241,20 @@ vt_cycle(int dir)
|
||||||
void
|
void
|
||||||
vt_feed(struct vt *v, int c)
|
vt_feed(struct vt *v, int c)
|
||||||
{
|
{
|
||||||
cursor_erase(v);
|
// The grid is the truth, so the old cursor cell can be repainted from it
|
||||||
|
// *after* the byte is consumed -- which lets two cases cost nothing:
|
||||||
|
// - a byte inside an escape sequence changes nothing on screen;
|
||||||
|
// - a plain glyph is drawn over the cell the cursor sat on, which repaints
|
||||||
|
// it anyway (unless a wrap is pending, when it lands on the next line).
|
||||||
|
int mid = v->esc_state != VE_NORMAL;
|
||||||
|
int over = !mid && !v->wrap_pending && c >= 0x20 && c < 0x7f &&
|
||||||
|
v->u8_need == 0;
|
||||||
vt_feed_1(v, c);
|
vt_feed_1(v, c);
|
||||||
|
if (mid && v->esc_state != VE_NORMAL)
|
||||||
|
return;
|
||||||
|
if (over)
|
||||||
|
v->curs_drawn = 0;
|
||||||
|
else
|
||||||
|
cursor_erase(v);
|
||||||
cursor_draw(v);
|
cursor_draw(v);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,9 @@ unsigned long kpi_jiffies_now(void);
|
||||||
// Output. The KPI is inside a process with no libc, and every implementation
|
// Output. The KPI is inside a process with no libc, and every implementation
|
||||||
// wants to be able to say what it did.
|
// wants to be able to say what it did.
|
||||||
void kpi_log(const char *s);
|
void kpi_log(const char *s);
|
||||||
|
// Says "module+0xoffset" for an address in a loaded module; set by the host.
|
||||||
|
extern void (*kpi_where)(unsigned long addr);
|
||||||
|
|
||||||
void kpi_lognum(long long v);
|
void kpi_lognum(long long v);
|
||||||
void kpi_loghex(unsigned long long v, int digits);
|
void kpi_loghex(unsigned long long v, int digits);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
// have been easier to find.
|
// have been easier to find.
|
||||||
|
|
||||||
#include "core/types.h"
|
#include "core/types.h"
|
||||||
|
#include "uapi/user.h"
|
||||||
#include "kpi.h"
|
#include "kpi.h"
|
||||||
|
|
||||||
void *
|
void *
|
||||||
|
|
@ -135,6 +136,8 @@ kpi_memcpy_toio(void *dst, const void *s, unsigned long n)
|
||||||
// __fortify_panic is what a driver's bounds-checked string helpers call when
|
// __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,
|
// 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.
|
// and the honest thing is to say so rather than return.
|
||||||
|
void (*kpi_where)(unsigned long addr);
|
||||||
|
|
||||||
void
|
void
|
||||||
kpi_fortify_panic(unsigned int reason, unsigned long avail, unsigned long size)
|
kpi_fortify_panic(unsigned int reason, unsigned long avail, unsigned long size)
|
||||||
{
|
{
|
||||||
|
|
@ -147,8 +150,17 @@ kpi_fortify_panic(unsigned int reason, unsigned long avail, unsigned long size)
|
||||||
kpi_log(" reason "); kpi_lognum(reason);
|
kpi_log(" reason "); kpi_lognum(reason);
|
||||||
kpi_log(", available "); kpi_lognum((long long)avail);
|
kpi_log(", available "); kpi_lognum((long long)avail);
|
||||||
kpi_log(", wanted "); kpi_lognum((long long)size);
|
kpi_log(", wanted "); kpi_lognum((long long)size);
|
||||||
kpi_log(", from 0x"); kpi_loghex((unsigned long)__builtin_return_address(0), 16);
|
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");
|
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 (;;)
|
for (;;)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue