panel-boot.patch -- NetBSD 11.0/x86: draw the Whistle logo and "NetBSD " on a Whistle InterJet's front panel (com1) as soon as the kernel starts, as WhistleWare's own kernel did. Enable with "options INTERJET_PANEL_BOOT". Apply in usr/src with: patch -p0 The bitmap ijpanel_logo[] is Whistle Communications' logo, converted from glyph 1 of the font interjet.pfn in WhistleWare 3.0.3. It is reproduced for preservation, is not this project's work, is not covered by the license below, and will be removed on request from a rightsholder. Author: Royce Williams License: the same terms as each NetBSD source file it modifies. --- sys/arch/x86/conf/files.x86.orig +++ sys/arch/x86/conf/files.x86 @@ -25,6 +25,12 @@ defflag PCPU_IDT +# Draw the Whistle logo on the Whistle InterJet front panel (com1) from +# consinit(); with INTERJET_PANEL_NETBOOT as well, a text frame named by +# boothowto instead. interjet-netbsd patches/panel-boot.patch. +defflag opt_interjet_panel.h INTERJET_PANEL_BOOT +defflag opt_interjet_panel.h INTERJET_PANEL_NETBOOT + define cpubus { [apid = -1] } define cpufeaturebus {} define ioapicbus { [apid = -1] } --- sys/arch/x86/x86/consinit.c.orig +++ sys/arch/x86/x86/consinit.c @@ -32,6 +32,7 @@ #include "opt_kgdb.h" #include "opt_puc.h" #include "opt_xen.h" +#include "opt_interjet_panel.h" #include #include @@ -154,6 +155,236 @@ #endif /* KGDB */ +#ifdef INTERJET_PANEL_BOOT +#include /* inb(), outb() */ +#include /* com_lcr, LCR_DLAB, LSR_TXRDY */ +#ifdef INTERJET_PANEL_NETBOOT +#include /* RB_SINGLE, RB_INITNAME */ +#endif + +/* + * interjet_panel_boot: + * Local change. Draw the Whistle + * logo on the Whistle InterJet front panel, com1 at 0x2f8, as soon as + * the kernel runs, so the panel stops showing the BIOS's last POST line, + * with "WhistleJet" on row 8 to the right of the whistle, its caption. + * The /etc/rc hook, a separate local change, then names each + * rc.d script in its place, and lcdd replaces that. WhistleWare's drew the + * logo at this point in the boot. The caption starts at ^A 58, cell 9: + * text lands only at ^A 4 + 6c (58 shown on the glass; 60 put it in + * cell 0, 2026-10-06). It ends at cell 18, short of the row's last + * cell, where a wrap would scroll the logo off the panel. + * + * With INTERJET_PANEL_NETBOOT, a kernel the netboot ROM loads draws a + * text frame instead of the logo, so the ROM's rows do not stand through + * the boot: the toolkit's four modes, which toolkit/mkimage.sh tells + * apart by boothowto (0 install, RB_INITNAME check, __RB_UNUSED1 reset + * password, both of them image drive: bits NetBSD 11.0 does not + * read), or with RB_SINGLE, every + * other netboot payload, "NFS boot" and where the shell is. boothowto + * is set by native_loader() before init386() reaches consinit(). The + * frame is the docs/panel/lcd.md 19 one: the title inverted and centred + * on row 1, CR LF to each row after it, every row padded to 20 cells, + * and nothing on row 8. + * + * Write only. The panel sends XON, XOFF and keys unprompted; reading + * the data register would take them from com(4), which owns the port + * once it attaches. The rate is the firmware's 57600 and is never set + * here. With DLAB set in LCR, a write to the data register would load + * the divisor instead, so nothing is written; an absent UART reads 0xff + * there, which stops it too. Each byte waits for THRE with the bound + * com_common_putc() uses, and the first timeout ends the screen, so a + * UART that never drains costs one bound, not one per byte. + * + * ijpanel_logo[] is converted from WhistleWare 3.0.3's font + * interjet.pfn, glyph 1: Whistle Communications' logo, not covered by + * this change's license. Per page it is ^B page, + * ^A first column, ^C count and that many bitmap bytes, page 7 first so + * that the row-8 text does not follow a run on its own page. No ESC is + * sent outside a counted ^C run, and no 0x1b in it is followed by W or + * X, which would arm the panel's reset watchdog or power the machine off. + * + * Author: Royce Williams + */ +#define IJPANEL_BASE 0x2f8 /* com1, the panel (IO_COM2) */ +#define IJPANEL_TIMO 150000 /* LSR polls per byte */ +#define IJPANEL_COLS 20 /* text cells a row */ + +#ifndef INTERJET_PANEL_NETBOOT +static const uint8_t ijpanel_logo[] = { + /* page 7, x 26..45: ^B ^A ^C, 20 bytes */ + 0x02, 7, 0x01, 26, 0x03, 20, + 0x01, 0x01, 0x03, 0x03, 0x02, 0x02, 0x06, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x3e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x3e, 0x0c, + /* page 0, x 46..72: ^B ^A ^C, 27 bytes */ + 0x02, 0, 0x01, 46, 0x03, 27, + 0x80, 0x80, 0x80, 0xc0, 0xc0, 0xe0, 0xe0, 0xf0, 0x70, 0x78, 0x78, 0x38, + 0x3c, 0x3c, 0x1c, 0x1c, 0x18, 0x38, 0x30, 0x70, 0x60, 0xe0, 0xc0, 0xc0, + 0xc0, 0x80, 0x80, + /* page 1, x 40..88: ^B ^A ^C, 49 bytes */ + 0x02, 1, 0x01, 40, 0x03, 49, + 0x0c, 0xfe, 0xfe, 0x8e, 0x0f, 0x1f, 0x1b, 0x3b, 0x31, 0x71, 0x61, 0xe0, + 0xe0, 0xc0, 0xc0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x80, 0xc0, 0xc0, 0xe1, 0xe1, 0xe3, 0xf3, 0xe7, 0xc7, 0xce, + 0x8e, 0x8e, 0x1c, 0x1c, 0x38, 0x38, 0x78, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, + 0x80, + /* page 2, x 41..101: ^B ^A ^C, 61 bytes */ + 0x02, 2, 0x01, 41, 0x03, 61, + 0x7f, 0xff, 0xff, 0x80, 0x00, 0xf8, 0xc0, 0x00, 0xe0, 0xc0, 0x00, 0x00, + 0x01, 0x01, 0x03, 0x03, 0x07, 0x07, 0x06, 0x0e, 0x0e, 0x1c, 0x1c, 0x39, + 0x39, 0x7b, 0xf3, 0xf3, 0xe7, 0xc7, 0xc7, 0x87, 0x03, 0x03, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, + 0x3f, 0x7e, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, + 0x80, + /* page 3, x 39..105: ^B ^A ^C, 67 bytes */ + 0x02, 3, 0x01, 39, 0x03, 67, + 0x80, 0x80, 0x80, 0xc0, 0xc0, 0xc0, 0xe0, 0xe1, 0x63, 0x30, 0x13, 0x87, + 0xe6, 0xe0, 0xf0, 0xf0, 0x78, 0x78, 0x38, 0x38, 0x38, 0x38, 0x70, 0x70, + 0x60, 0xe0, 0xc0, 0x80, 0x00, 0x01, 0x01, 0x03, 0x07, 0x1f, 0x3f, 0xfe, + 0xfc, 0xf8, 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf8, 0xe0, + /* page 4, x 24..105: ^B ^A ^C, 82 bytes */ + 0x02, 4, 0x01, 24, 0x03, 82, + 0x80, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0x70, 0x78, 0x38, 0x3c, 0x1c, 0x1e, + 0x0e, 0x0f, 0x0f, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0xfc, 0xc0, 0x80, 0x80, 0x80, 0x8c, 0x9e, 0x9e, 0xc0, 0xf3, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x3f, 0x0f, 0x01, + /* page 5, x 19..100: ^B ^A ^C, 82 bytes */ + 0x02, 5, 0x01, 19, 0x03, 82, + 0xe0, 0xf0, 0x7c, 0x3e, 0x0f, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x1f, 0x7f, 0xfe, 0xf0, 0xe0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0xff, 0xff, 0x3f, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0xe1, 0xf3, 0xfb, 0xff, + 0xff, 0x7f, 0x3f, 0x3f, 0x1f, 0x1f, 0x0f, 0x07, 0x03, 0x03, + /* page 6, x 19..88: ^B ^A ^C, 70 bytes */ + 0x02, 6, 0x01, 19, 0x03, 70, + 0x0f, 0x1f, 0x3e, 0x70, 0x60, 0xc0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, + 0x07, 0x0f, 0x0e, 0x1e, 0x1c, 0x18, 0x38, 0x38, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x38, 0x18, 0x1c, 0x1c, 0x0e, 0x0f, 0x07, 0x03, 0x09, 0x08, 0x0c, + 0x0c, 0x0c, 0x06, 0x06, 0x07, 0x03, 0x03, 0x03, 0x01, 0x01, + +}; +#endif /* ! INTERJET_PANEL_NETBOOT */ + +/* one byte to the panel; 0 if the transmitter never drained */ +static int +ijpanel_putc(int c) +{ + int timo; + + for (timo = IJPANEL_TIMO; + ! (inb(IJPANEL_BASE + com_lsr) & LSR_TXRDY); ) + if (--timo == 0) + return 0; + outb(IJPANEL_BASE + com_data, c); + return 1; +} + +#ifdef INTERJET_PANEL_NETBOOT +/* + * One row of a text frame, from its first cell: CR, then S, centred if + * CENTRE (left pad (20 - length) / 2, rounded down), padded with + * written spaces to all 20 cells and clipped there. INV inverts it + * with ^O, and ^N follows, so no inversion outlives the row. 0 if the + * transmitter never drained. + */ +static int +ijpanel_row(const char *s, int centre, int inv) +{ + size_t len = strlen(s), col = 0, pad = 0; + + if (centre && len < IJPANEL_COLS) + pad = (IJPANEL_COLS - len) / 2; + if (! ijpanel_putc('\r') || (inv && ! ijpanel_putc(0x0f))) + return 0; + for (; col < pad; col++) /* written, so a bar is solid */ + if (! ijpanel_putc(' ')) + return 0; + for (; col < IJPANEL_COLS && *s != '\0'; col++, s++) + if (! ijpanel_putc(*s)) + return 0; + for (; col < IJPANEL_COLS; col++) /* over the ROM's old row */ + if (! ijpanel_putc(' ')) + return 0; + return ! inv || ijpanel_putc(0x0e); +} +#endif /* INTERJET_PANEL_NETBOOT */ + +static void +interjet_panel_boot(void) +{ + /* ^N inversion off, ^L clear and home */ + static const uint8_t head[] = { 0x0e, 0x0c }; +#ifdef INTERJET_PANEL_NETBOOT + /* rows 1 to 6; row 1 the title. Rows 7 and 8 stay as ^L left them */ + const char *row[6] = { "", "", "", "", "", "" }; + int nrows, r; +#else + /* ^B 7 ^A 58: row 8, cell 9 (^A 4 + 6c), right of the whistle */ + static const uint8_t slot[] = { 0x02, 7, 0x01, 58 }; + static const char caption[] = "WhistleJet"; /* cells 9 to 18 */ +#endif + size_t i; + + /* DLAB set, or no UART at all: writing would not reach the panel */ + if (inb(IJPANEL_BASE + com_lcr) & LCR_DLAB) + return; + for (i = 0; i < sizeof(head); i++) + if (! ijpanel_putc(head[i])) + return; +#ifdef INTERJET_PANEL_NETBOOT + if (boothowto & RB_SINGLE) { + /* every netboot payload but the toolkit's: a shell on com0 */ + row[0] = "NFS boot"; + /* the kernel's own name, less the prefix every one shares */ + row[1] = kernel_ident; + if (strncmp(row[1], "INTERJET_", 9) == 0) + row[1] += 9; + row[4] = "Single user: shell"; + row[5] = "on the COM0 console."; + nrows = 6; + } else { + /* the toolkit's modes: toolkit/mkimage.sh's boothowto */ + if ((boothowto & (RB_INITNAME | __RB_UNUSED1)) == + (RB_INITNAME | __RB_UNUSED1)) + row[0] = "Image drive"; + else if (boothowto & __RB_UNUSED1) + row[0] = "Reset password"; + else if (boothowto & RB_INITNAME) + row[0] = "Check OS"; + else + row[0] = "Install"; + row[3] = "Please wait..."; + nrows = 4; + } + /* the title inverted and centred; row 4's wait centred; the rest left */ + for (r = 0; r < nrows; r++) + if ((r > 0 && (! ijpanel_putc('\r') || ! ijpanel_putc('\n'))) || + ! ijpanel_row(row[r], r == 0 || r == 3, r == 0)) + return; /* give up on the screen */ +#else + for (i = 0; i < sizeof(ijpanel_logo); i++) + if (! ijpanel_putc(ijpanel_logo[i])) + return; /* give up on the screen */ + for (i = 0; i < sizeof(slot); i++) + if (! ijpanel_putc(slot[i])) + return; + /* never a CR or LF on row 8: a LF there scrolls the panel */ + for (i = 0; caption[i] != '\0'; i++) + if (! ijpanel_putc(caption[i])) + return; +#endif +} +#endif /* INTERJET_PANEL_BOOT */ + /* * consinit: * initialize the system console. @@ -191,6 +422,11 @@ return; initted = 1; +#ifdef INTERJET_PANEL_BOOT + /* once, before the console: every path below passes here */ + interjet_panel_boot(); +#endif + #ifndef CONS_OVERRIDE consinfo = lookup_bootinfo(BTINFO_CONSOLE); if (!consinfo)