- desktop boot screen: typewriter effect removed, whole screen drawn in one pass - login: ohMyLogo (DEAICUP lotus, 288px embedded) intro screen before password card - strict error policy instrumentation (zero degradation): login LOGIN-E01..E05 (integrity/badctx/noconf/nouser/nohash) and desktop DSK-E01 now full-screen fatal with symbol + halt instead of silent skip - root-cause fixed: missing .build_tmp/mkfat32.ps1 silently left SATA sidecar stale (login.elf 'not found' degradation) - replaced with CODE/linux/make_sata_sidecar.sh (auto adaptive FAT32 rebuild each build) - recreated lost CODE/login/patch_login_hash.ps1 (.text SHA256 -> .hashseg) - dev config now full flow: show_logo=1, wizard+login enabled, dev shell off
940 lines
35 KiB
C
940 lines
35 KiB
C
/* login.elf — Deshab login screen
|
||
* Shown on non-first boot (firstInit=1) before loading desktop.
|
||
* Reads USER.CONF via boot context (ctx->reserved[0/1]),
|
||
* extracts username + password hash, prompts for password,
|
||
* verifies via SHA256 comparison, sets ctx->reserved[2]=1 on success.
|
||
*
|
||
* 防篡改:对 .text 段做 SHA256 自校验,预期哈希由构建后补丁脚本
|
||
* 写入 .hashseg 段(__expected_hash)。运行时若发现哈希不匹配,
|
||
* 拒绝继续并触发串口告警。开发期哈希为全 0 时跳过校验。
|
||
*
|
||
* Returns to DSK in all cases (success, skip via Esc, or fatal error).
|
||
* DSK loads desktop.elf after this returns.
|
||
*/
|
||
|
||
#include "../UTSM/include/utsm/dsk.h"
|
||
|
||
/* 防篡改:linker 导出的 .text 段范围与预期哈希存储位置 */
|
||
extern const u8 __text_start[];
|
||
extern const u8 __text_end[];
|
||
extern const u8 __expected_hash[];
|
||
extern const u8 __expected_hash_end[];
|
||
|
||
typedef signed char i8;
|
||
typedef unsigned char u8;
|
||
typedef unsigned short u16;
|
||
typedef unsigned int u32;
|
||
typedef unsigned long long u64;
|
||
typedef long long i64;
|
||
|
||
#define COM1 0x3F8
|
||
|
||
static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); }
|
||
static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
|
||
|
||
/* ---- TSC-based timing (PIT-calibrated) ---- */
|
||
static u64 g_tsc_per_ms = 0;
|
||
|
||
static __inline__ u64 rdtsc_fi(void) {
|
||
u32 lo, hi;
|
||
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
|
||
return ((u64)hi << 32) | lo;
|
||
}
|
||
|
||
static void tsc_calibrate_fi(void) {
|
||
outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
|
||
outb(0x40, 0x7c); /* 11932 low = ~10ms */
|
||
outb(0x40, 0x2e); /* 11932 high */
|
||
u64 tsc_start = rdtsc_fi();
|
||
u16 prev = 0; u64 loops = 0;
|
||
for (;;) {
|
||
outb(0x43, 0x00);
|
||
u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8);
|
||
if (cur > prev && loops > 10) break;
|
||
prev = cur; loops++;
|
||
}
|
||
u64 tsc_end = rdtsc_fi();
|
||
g_tsc_per_ms = (tsc_end - tsc_start) / 10;
|
||
}
|
||
|
||
static void delay_ms_fi(u32 ms) {
|
||
if (!g_tsc_per_ms) {
|
||
for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause");
|
||
return;
|
||
}
|
||
u64 target = g_tsc_per_ms * ms;
|
||
u64 start = rdtsc_fi();
|
||
while (rdtsc_fi() - start < target) __asm__ volatile("pause");
|
||
}
|
||
|
||
static void delay_frame(void) { delay_ms_fi(33); }
|
||
|
||
/* ---- Serial logging (COM1) ---- */
|
||
static void sputc(char c) {
|
||
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
|
||
outb(COM1, (unsigned char)c);
|
||
}
|
||
static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } }
|
||
static void logl(const char *s) { swrite(s); swrite("\n"); }
|
||
|
||
/* ---- Colors (dark theme matching FirstInit) ---- */
|
||
#define BG_TOP 0xFF2D2D30u
|
||
#define BG_BOTTOM 0xFF1A1A1Eu
|
||
#define CARD_BG 0xFF353539u
|
||
#define INPUT_BG 0xFF2A2A2Eu
|
||
#define TEXT_FG 0xFFE0E0E0u
|
||
#define ACCENT 0xFF5A8AC8u
|
||
#define ERROR_FG 0xFFE05050u
|
||
#define DIM_FG 0xFF808088u
|
||
|
||
static u64 fb_a, fb_w, fb_h, fb_p;
|
||
|
||
static u32 blend(u32 c1, u32 c2, u32 a) {
|
||
u32 na=256-a;
|
||
u32 r=((c1&0xFF)*na+(c2&0xFF)*a)>>8;
|
||
u32 g=(((c1>>8)&0xFF)*na+((c2>>8)&0xFF)*a)>>8;
|
||
u32 b=(((c1>>16)&0xFF)*na+((c2>>16)&0xFF)*a)>>8;
|
||
return 0xFF000000|(b<<16)|(g<<8)|r;
|
||
}
|
||
|
||
/* ---- ASCII font (Consolas 18px grayscale) — shared from firstInit ---- */
|
||
#include "../firstInit/ascii_bitmaps.c"
|
||
#define ASCII_W 11
|
||
#define ASCII_H 18
|
||
#define ASCII_STEP 11
|
||
|
||
static void fb_char(u32 *fb, u32 ch, i64 x, i64 y, u32 fg, u32 bg) {
|
||
if (ch < ' ' || ch > '~') ch = ' ';
|
||
u32 idx = (u32)(ch - ' ');
|
||
const u8 *g = g_ascii[idx];
|
||
for (i64 r=0; r<ASCII_H; r++) {
|
||
i64 yy = y + r;
|
||
if (yy < 0 || (u64)yy >= fb_h) continue;
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
|
||
for (i64 c=0; c<ASCII_W; c++) {
|
||
u32 a = g[r * ASCII_W + c];
|
||
if (a == 0) continue;
|
||
i64 xx = x + c;
|
||
if (xx < 0 || (u64)xx >= fb_w) continue;
|
||
line[(u64)xx] = (a == 255) ? fg : blend(bg, fg, a);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void fb_text(u32 *fb, const char *s, i64 x, i64 y, u32 fg, u32 bg) {
|
||
for (i64 i=0; s[i]; i++) fb_char(fb, (u32)(u8)s[i], x+i*ASCII_STEP, y, fg, bg);
|
||
}
|
||
|
||
static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color);
|
||
static void fill_gradient_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h);
|
||
static u64 lf_strlen(const char *s) { u64 n = 0; while (s[n]) n++; return n; }
|
||
|
||
/* ===================================================================
|
||
* 严格错误策略插桩:任何错误(含未实现路径)不再降级、不再静默跳过,
|
||
* 必须整屏 fatal 显示错误码后停机。"疯但好使" 的反面保障:
|
||
* 错也错得有落款。每个插桩点一个唯一 symbol(LOGIN-E<nn>)。
|
||
* =================================================================== */
|
||
static void login_fatal(const char *sym) {
|
||
logl(sym);
|
||
if (fb_a) {
|
||
u32 *fb = (u32 *)(u64)fb_a;
|
||
fill_rect(fb, 0, 0, (i64)fb_w, (i64)fb_h, 0xFF3A0E14u);
|
||
i64 cx = ((i64)fb_w - (i64)lf_strlen(sym) * ASCII_STEP) / 2;
|
||
fb_text(fb, sym, cx, (i64)fb_h / 2 - 40, 0xFFF2D8D8u, 0);
|
||
fb_text(fb, "DESHAB LOGIN FATAL - SYSTEM HALTED",
|
||
((i64)fb_w - 36 * ASCII_STEP) / 2, (i64)fb_h / 2, 0xFFB08890u, 0);
|
||
}
|
||
for (;;) __asm__("cli; hlt");
|
||
}
|
||
|
||
/* ohMyLogo 开场:登录卡片之前显示 DEAICUP 莲花 Logo(ohMyLogo.png 内嵌) */
|
||
#include "ohmylogo_data.c"
|
||
|
||
static void draw_logo_rgba(u32 *fb, i64 x, i64 y) {
|
||
for (i64 r = 0; r < g_panic_logo_h; r++) {
|
||
for (i64 c = 0; c < g_panic_logo_w; c++) {
|
||
const unsigned char *px4 =
|
||
g_panic_logo_rgba + ((u64)r * g_panic_logo_w + (u64)c) * 4;
|
||
u32 a = px4[3];
|
||
if (a < 8) continue;
|
||
u32 col = 0xFF000000u | ((u32)px4[0] << 16) | ((u32)px4[1] << 8) | px4[2];
|
||
if (a < 250) {
|
||
/* 简单 alpha 混合到深蓝底 */
|
||
u32 bg = 0xFF020F22u;
|
||
u32 na = 256 - a;
|
||
u32 rr = (((bg >> 16) & 0xFF) * na + ((col >> 16) & 0xFF) * a) >> 8;
|
||
u32 gg = (((bg >> 8) & 0xFF) * na + ((col >> 8) & 0xFF) * a) >> 8;
|
||
u32 bb = ((bg & 0xFF) * na + (col & 0xFF) * a) >> 8;
|
||
col = 0xFF000000u | (rr << 16) | (gg << 8) | bb;
|
||
}
|
||
i64 xx = x + c, yy = y + r;
|
||
if (xx < 0 || (u64)xx >= fb_w || yy < 0 || (u64)yy >= fb_h) continue;
|
||
*(u32 *)((u8 *)fb + (u64)yy * fb_p + (u64)xx * 4) = col;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void logo_intro_screen(u32 *fb) {
|
||
fill_gradient_rect(fb, 0, 0, (i64)fb_w, (i64)fb_h);
|
||
i64 lx = ((i64)fb_w - g_panic_logo_w) / 2;
|
||
i64 ly = ((i64)fb_h - g_panic_logo_h) / 2 - 30;
|
||
if (ly < 0) ly = 0;
|
||
draw_logo_rgba(fb, lx, ly);
|
||
fb_text(fb, "DESHAB LOGIN", ((i64)fb_w - 12 * ASCII_STEP) / 2,
|
||
ly + g_panic_logo_h + 16, 0xFFF0D080u, 0);
|
||
for (int i = 0; i < 40; i++) delay_frame(); /* ~1.3s 开场 */
|
||
}
|
||
|
||
|
||
static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) {
|
||
for (i64 r=0; r<h; r++) {
|
||
i64 yy = y + r;
|
||
if (yy < 0 || (u64)yy >= fb_h) continue;
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
|
||
for (i64 c=0; c<w; c++) {
|
||
i64 xx = x + c;
|
||
if (xx < 0 || (u64)xx >= fb_w) continue;
|
||
line[(u64)xx] = color;
|
||
}
|
||
}
|
||
}
|
||
|
||
static u32 bg_at_y(i64 y) {
|
||
if (fb_h <= 1) return BG_TOP;
|
||
if (y < 0) y = 0;
|
||
if ((u64)y >= fb_h) y = (i64)fb_h - 1;
|
||
return blend(BG_TOP, BG_BOTTOM, (u32)(((u64)y * 255ULL) / (fb_h - 1)));
|
||
}
|
||
|
||
static void fill_gradient_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) {
|
||
for (i64 r=0; r<h; r++) {
|
||
i64 yy = y + r;
|
||
if (yy < 0 || (u64)yy >= fb_h) continue;
|
||
u32 color = bg_at_y(yy);
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
|
||
for (i64 c=0; c<w; c++) {
|
||
i64 xx = x + c;
|
||
if (xx < 0 || (u64)xx >= fb_w) continue;
|
||
line[(u64)xx] = color;
|
||
}
|
||
}
|
||
}
|
||
|
||
static int rounded_rect_contains(i64 px, i64 py, i64 x, i64 y, i64 w, i64 h, i64 radius) {
|
||
if (px < x || py < y || px >= x + w || py >= y + h) return 0;
|
||
if (radius <= 0) return 1;
|
||
if (radius * 2 > w) radius = w / 2;
|
||
if (radius * 2 > h) radius = h / 2;
|
||
i64 left = x + radius;
|
||
i64 right = x + w - radius - 1;
|
||
i64 top = y + radius;
|
||
i64 bottom = y + h - radius - 1;
|
||
if (px >= left && px <= right) return 1;
|
||
if (py >= top && py <= bottom) return 1;
|
||
i64 cx = px < left ? left : right;
|
||
i64 cy = py < top ? top : bottom;
|
||
i64 dx = px - cx;
|
||
i64 dy = py - cy;
|
||
return dx * dx + dy * dy <= radius * radius;
|
||
}
|
||
|
||
static void fill_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, u32 color) {
|
||
if (w <= 0 || h <= 0) return;
|
||
for (i64 r=0; r<h; r++) {
|
||
i64 yy = y + r;
|
||
if (yy < 0 || (u64)yy >= fb_h) continue;
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
|
||
for (i64 c=0; c<w; c++) {
|
||
i64 xx = x + c;
|
||
if (xx < 0 || (u64)xx >= fb_w) continue;
|
||
if (rounded_rect_contains(xx, yy, x, y, w, h, radius)) line[(u64)xx] = color;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void stroke_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, i64 thickness, u32 color) {
|
||
if (w <= 0 || h <= 0 || thickness <= 0) return;
|
||
for (i64 r=0; r<h; r++) {
|
||
i64 yy = y + r;
|
||
if (yy < 0 || (u64)yy >= fb_h) continue;
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
|
||
for (i64 c=0; c<w; c++) {
|
||
i64 xx = x + c;
|
||
if (xx < 0 || (u64)xx >= fb_w) continue;
|
||
int outer = rounded_rect_contains(xx, yy, x, y, w, h, radius);
|
||
int inner = rounded_rect_contains(xx, yy, x + thickness, y + thickness, w - thickness * 2, h - thickness * 2, radius - thickness);
|
||
if (outer && !inner) line[(u64)xx] = color;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void draw_card(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 card) {
|
||
fill_rounded_rect(fb, x, y, w, h, 4, card);
|
||
}
|
||
|
||
static void draw_rounded_input(u32 *fb, i64 x, i64 y, i64 w, i64 h, const char *value, int mask, u32 fg, u32 card_bg, int active) {
|
||
(void)card_bg;
|
||
u32 fill = active ? 0xFF45454Au : INPUT_BG;
|
||
fill_rounded_rect(fb, x, y, w, h, 2, fill);
|
||
stroke_rounded_rect(fb, x, y, w, h, 2, 1, active ? 0xFF6C6C72 : 0xFF404044);
|
||
char out[80]; int i=0;
|
||
while(value[i] && i<76) { out[i] = mask ? '*' : value[i]; i++; }
|
||
out[i]=0;
|
||
if (out[0]) fb_text(fb, out, x+14, y+(h-ASCII_H)/2, fg, fill);
|
||
if (active) {
|
||
i64 cx2 = x + 14 + i * ASCII_STEP;
|
||
fill_rect(fb, cx2, y+10, 2, h-20, fg);
|
||
}
|
||
}
|
||
|
||
/* ---- PS/2 鼠标光标 ---- */
|
||
#define CURSZ 16
|
||
static int g_mouse_x = 400, g_mouse_y = 300;
|
||
static int g_mouse_btn = 0;
|
||
static u32 g_cursor_bg[CURSZ * CURSZ];
|
||
static int g_cursor_saved = 0;
|
||
static int g_cursor_old_x = -1, g_cursor_old_y = -1;
|
||
static u8 g_mouse_buf[3];
|
||
static int g_mouse_idx = 0;
|
||
|
||
/* 16×16 箭头光标(与 desktop 一致) */
|
||
static const u8 cursor_shape[16][16] = {
|
||
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||
{1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
|
||
{1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
|
||
{1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
|
||
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0},
|
||
{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0},
|
||
{1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
|
||
{1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0},
|
||
{1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0},
|
||
{1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0},
|
||
{1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0},
|
||
{0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0},
|
||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||
};
|
||
|
||
static void cursor_save_bg(u32 *fb, int mx, int my) {
|
||
for (int r = 0; r < CURSZ; r++) {
|
||
for (int c = 0; c < CURSZ; c++) {
|
||
int x = mx + c, y = my + r;
|
||
if (x >= 0 && (u64)x < fb_w && y >= 0 && (u64)y < fb_h) {
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p);
|
||
g_cursor_bg[r * CURSZ + c] = line[x];
|
||
} else {
|
||
g_cursor_bg[r * CURSZ + c] = 0;
|
||
}
|
||
}
|
||
}
|
||
g_cursor_saved = 1;
|
||
g_cursor_old_x = mx;
|
||
g_cursor_old_y = my;
|
||
}
|
||
|
||
static void cursor_restore_bg(u32 *fb) {
|
||
if (!g_cursor_saved) return;
|
||
for (int r = 0; r < CURSZ; r++) {
|
||
for (int c = 0; c < CURSZ; c++) {
|
||
int x = g_cursor_old_x + c, y = g_cursor_old_y + r;
|
||
if (x >= 0 && (u64)x < fb_w && y >= 0 && (u64)y < fb_h) {
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p);
|
||
line[x] = g_cursor_bg[r * CURSZ + c];
|
||
}
|
||
}
|
||
}
|
||
g_cursor_saved = 0;
|
||
}
|
||
|
||
static void cursor_draw(u32 *fb, int mx, int my) {
|
||
u32 fg = ACCENT;
|
||
for (int r = 0; r < CURSZ; r++) {
|
||
for (int c = 0; c < CURSZ; c++) {
|
||
if (!cursor_shape[r][c]) continue;
|
||
int x = mx + c, y = my + r;
|
||
if (x >= 0 && (u64)x < fb_w && y >= 0 && (u64)y < fb_h) {
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p);
|
||
line[x] = fg;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static int ps2_mouse_poll(void) {
|
||
u8 st = inb(0x64);
|
||
if (!(st & 1)) return 0;
|
||
if (!(st & 0x20)) return 0;
|
||
u8 data = inb(0x60);
|
||
g_mouse_buf[g_mouse_idx++] = data;
|
||
if (g_mouse_idx < 3) return 0;
|
||
g_mouse_idx = 0;
|
||
if (!(g_mouse_buf[0] & 0x08)) return 0;
|
||
int dx = (int)(i8)g_mouse_buf[1];
|
||
int dy = -(int)(i8)g_mouse_buf[2];
|
||
g_mouse_x += dx;
|
||
g_mouse_y += dy;
|
||
if (g_mouse_x < 0) g_mouse_x = 0;
|
||
if (g_mouse_y < 0) g_mouse_y = 0;
|
||
if (g_mouse_x >= (int)fb_w - CURSZ) g_mouse_x = (int)fb_w - CURSZ;
|
||
if (g_mouse_y >= (int)fb_h - CURSZ) g_mouse_y = (int)fb_h - CURSZ;
|
||
int old_btn = g_mouse_btn;
|
||
g_mouse_btn = g_mouse_buf[0] & 0x03;
|
||
if (dx == 0 && dy == 0 && g_mouse_btn == old_btn) return 0;
|
||
return 1;
|
||
}
|
||
|
||
/* ---- PS/2 scan code set 1 → ASCII ---- */
|
||
static char scan_to_ascii(u8 sc, int shift) {
|
||
static const char normal[58] = {
|
||
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t',
|
||
'q','w','e','r','t','y','u','i','o','p','[',']','\n',0,
|
||
'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\',
|
||
'z','x','c','v','b','n','m',',','.','/',0,'*',0,' '
|
||
};
|
||
static const char shifted[58] = {
|
||
0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t',
|
||
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0,
|
||
'A','S','D','F','G','H','J','K','L',':','"','~',0,'|',
|
||
'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' '
|
||
};
|
||
if (sc >= 58) return 0;
|
||
return shift ? shifted[sc] : normal[sc];
|
||
}
|
||
|
||
/* ---- SHA-256 (compact, copied from FirstInit) ---- */
|
||
#define ROR(x,n) (((x)>>(n))|((x)<<(32-(n))))
|
||
static const u32 K256[64] = {0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2};
|
||
static void sha256(const char *msg, u8 out[32]) {
|
||
u32 h[8]={0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19};
|
||
u8 block[64]; u64 len=0; while(msg[len]) len++;
|
||
for(int i=0;i<64;i++) block[i]=0;
|
||
for(u64 i=0;i<len && i<55;i++) block[i]=(u8)msg[i];
|
||
block[len]=0x80; u64 bit=len*8;
|
||
for(int i=0;i<8;i++) block[63-i]=(u8)(bit>>(i*8));
|
||
u32 w[64];
|
||
for(int i=0;i<16;i++) w[i]=((u32)block[i*4]<<24)|((u32)block[i*4+1]<<16)|((u32)block[i*4+2]<<8)|block[i*4+3];
|
||
for(int i=16;i<64;i++){u32 s0=ROR(w[i-15],7)^ROR(w[i-15],18)^(w[i-15]>>3);u32 s1=ROR(w[i-2],17)^ROR(w[i-2],19)^(w[i-2]>>10);w[i]=w[i-16]+s0+w[i-7]+s1;}
|
||
u32 a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7];
|
||
for(int i=0;i<64;i++){u32 S1=ROR(e,6)^ROR(e,11)^ROR(e,25);u32 ch=(e&f)^((~e)&g);u32 t1=hh+S1+ch+K256[i]+w[i];u32 S0=ROR(a,2)^ROR(a,13)^ROR(a,22);u32 maj=(a&b)^(a&c)^(b&c);u32 t2=S0+maj;hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;}
|
||
h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh;
|
||
for(int i=0;i<8;i++){out[i*4]=(u8)(h[i]>>24);out[i*4+1]=(u8)(h[i]>>16);out[i*4+2]=(u8)(h[i]>>8);out[i*4+3]=(u8)h[i];}
|
||
}
|
||
|
||
/* ---- 防篡改:对任意字节缓冲计算 SHA256(分块处理,支持 >55 字节) ---- */
|
||
static void sha256_buf(const u8 *msg, u64 len, u8 out[32]) {
|
||
u32 h[8]={0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19};
|
||
u8 block[64];
|
||
u64 processed = 0;
|
||
while (processed + 64 <= len) {
|
||
for (int i = 0; i < 64; i++) block[i] = msg[processed + i];
|
||
u32 w[64];
|
||
for(int i=0;i<16;i++) w[i]=((u32)block[i*4]<<24)|((u32)block[i*4+1]<<16)|((u32)block[i*4+2]<<8)|block[i*4+3];
|
||
for(int i=16;i<64;i++){u32 s0=ROR(w[i-15],7)^ROR(w[i-15],18)^(w[i-15]>>3);u32 s1=ROR(w[i-2],17)^ROR(w[i-2],19)^(w[i-2]>>10);w[i]=w[i-16]+s0+w[i-7]+s1;}
|
||
u32 a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7];
|
||
for(int i=0;i<64;i++){u32 S1=ROR(e,6)^ROR(e,11)^ROR(e,25);u32 ch=(e&f)^((~e)&g);u32 t1=hh+S1+ch+K256[i]+w[i];u32 S0=ROR(a,2)^ROR(a,13)^ROR(a,22);u32 maj=(a&b)^(a&c)^(b&c);u32 t2=S0+maj;hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;}
|
||
h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh;
|
||
processed += 64;
|
||
}
|
||
/* 最终块:剩余数据 + 0x80 + 长度(64 位大端) */
|
||
u32 rem = (u32)(len - processed);
|
||
for (u32 i = 0; i < 64; i++) block[i] = 0;
|
||
for (u32 i = 0; i < rem; i++) block[i] = msg[processed + i];
|
||
block[rem] = 0x80;
|
||
u64 bit = len * 8;
|
||
for (int i = 0; i < 8; i++) block[63 - i] = (u8)(bit >> (i * 8));
|
||
u32 w[64];
|
||
for(int i=0;i<16;i++) w[i]=((u32)block[i*4]<<24)|((u32)block[i*4+1]<<16)|((u32)block[i*4+2]<<8)|block[i*4+3];
|
||
for(int i=16;i<64;i++){u32 s0=ROR(w[i-15],7)^ROR(w[i-15],18)^(w[i-15]>>3);u32 s1=ROR(w[i-2],17)^ROR(w[i-2],19)^(w[i-2]>>10);w[i]=w[i-16]+s0+w[i-7]+s1;}
|
||
u32 a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7];
|
||
for(int i=0;i<64;i++){u32 S1=ROR(e,6)^ROR(e,11)^ROR(e,25);u32 ch=(e&f)^((~e)&g);u32 t1=hh+S1+ch+K256[i]+w[i];u32 S0=ROR(a,2)^ROR(a,13)^ROR(a,22);u32 maj=(a&b)^(a&c)^(b&c);u32 t2=S0+maj;hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;}
|
||
h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh;
|
||
for(int i=0;i<8;i++){out[i*4]=(u8)(h[i]>>24);out[i*4+1]=(u8)(h[i]>>16);out[i*4+2]=(u8)(h[i]>>8);out[i*4+3]=(u8)h[i];}
|
||
}
|
||
|
||
/* ---- 防篡改:运行时 .text 段完整性校验 ----
|
||
* 计算 __text_start ~ __text_end 的 SHA256,与 __expected_hash 对比。
|
||
* 返回 1=校验通过(或开发期哈希为空跳过),0=校验失败(被篡改) */
|
||
static int login_verify_integrity(void) {
|
||
const u8 *start = __text_start;
|
||
const u8 *end = __text_end;
|
||
const u8 *expected = __expected_hash;
|
||
u64 len = (u64)(end - start);
|
||
if (len == 0) return 0;
|
||
|
||
/* 开发期便利:预期哈希全 0 时跳过校验 */
|
||
int all_zero = 1;
|
||
for (int i = 0; i < 32; i++) if (expected[i] != 0) { all_zero = 0; break; }
|
||
if (all_zero) {
|
||
logl("[login] integrity: expected hash empty (dev mode), skip");
|
||
return 1;
|
||
}
|
||
|
||
u8 actual[32];
|
||
sha256_buf(start, len, actual);
|
||
|
||
/* 常量时间比较 */
|
||
u8 diff = 0;
|
||
for (int i = 0; i < 32; i++) diff |= actual[i] ^ expected[i];
|
||
if (diff == 0) {
|
||
logl("[login] integrity: OK");
|
||
return 1;
|
||
}
|
||
logl("[login] integrity: FAIL — code tampered!");
|
||
return 0;
|
||
}
|
||
|
||
/* ---- conf parsing ----
|
||
* USER.CONF format (built by FirstInit's build_user_conf, but stored unencrypted
|
||
* on disk for login.elf to parse):
|
||
* DESHAB_USERCONF_V1
|
||
* computer=<pc>
|
||
* username=<user>
|
||
* passwordSha256=<64 hex chars>
|
||
* ...
|
||
*
|
||
* If the buffer does NOT start with "DESHAB_USERCONF_V1", it may be XOR-encrypted
|
||
* with the password hash stream. In that case, the user's entered password is
|
||
* used to decrypt: SHA256(password) → hash, then buf[i] ^= hash[i & 31].
|
||
* If the decrypted buffer starts with the magic, the password is correct.
|
||
*/
|
||
|
||
static int streq_n(const char *a, const char *b, u32 n) {
|
||
for (u32 i = 0; i < n; i++) {
|
||
if (a[i] != b[i]) return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* Find "key=" in buf, copy value (up to newline) into out (NUL-terminated).
|
||
* Returns 0 on success, -1 if key not found. */
|
||
static int conf_get_value(const u8 *buf, u32 size, const char *key, char *out, u32 out_cap) {
|
||
u32 klen = 0; while (key[klen]) klen++;
|
||
for (u32 i = 0; i + klen + 1 < size; i++) {
|
||
/* Match key at start of line (or start of buffer) */
|
||
if (i > 0 && buf[i-1] != '\n') continue;
|
||
if (buf[i + klen] != '=') continue;
|
||
if (!streq_n((const char *)buf + i, key, klen)) continue;
|
||
/* Found key=, copy value until newline or end */
|
||
u32 vstart = i + klen + 1;
|
||
u32 j = 0;
|
||
while (vstart + j < size && buf[vstart + j] != '\n' && buf[vstart + j] != '\r' && j + 1 < out_cap) {
|
||
out[j] = (char)buf[vstart + j];
|
||
j++;
|
||
}
|
||
out[j] = 0;
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static int starts_with(const u8 *buf, u32 size, const char *prefix) {
|
||
u32 i = 0;
|
||
while (prefix[i]) {
|
||
if (i >= size) return 0;
|
||
if (buf[i] != (u8)prefix[i]) return 0;
|
||
i++;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* ---- hex string comparison ---- */
|
||
static int hexeq(const u8 *hash32, const char *hex64) {
|
||
static const char hx[] = "0123456789abcdef";
|
||
for (int i = 0; i < 32; i++) {
|
||
if (hex64[i*2] != hx[hash32[i] >> 4]) return 0;
|
||
if (hex64[i*2+1] != hx[hash32[i] & 0xf]) return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* ---- login state ---- */
|
||
static char g_username[64];
|
||
static char g_password_hash[80]; /* 64 hex chars + NUL */
|
||
static int g_has_user; /* 1 if username extracted */
|
||
static int g_has_hash; /* 1 if passwordSha256 extracted */
|
||
static int g_is_encrypted; /* 1 if conf buffer is XOR-encrypted */
|
||
|
||
/* ---- UI layout ---- */
|
||
#define CARD_W 560
|
||
#define CARD_H 400
|
||
|
||
static void draw_login_card(u32 *fb, i64 card_x, i64 card_y, const char *pass, int active,
|
||
const char *msg, u32 msg_color, u32 fg) {
|
||
u32 card = CARD_BG;
|
||
u32 sub_fg = 0xFF9098A0;
|
||
/* Background + card */
|
||
cursor_restore_bg(fb);
|
||
fill_gradient_rect(fb, card_x - 24, card_y - 24, CARD_W + 48, CARD_H + 48);
|
||
draw_card(fb, card_x, card_y, CARD_W, CARD_H, card);
|
||
/* Title */
|
||
fb_text(fb, "Deshab Login", card_x + 40, card_y + 40, fg, card);
|
||
fb_text(fb, "Enter your password to continue", card_x + 40, card_y + 70, sub_fg, card);
|
||
/* Username field */
|
||
fb_text(fb, "Username", card_x + 40, card_y + 120, sub_fg, card);
|
||
draw_rounded_input(fb, card_x + 40, card_y + 150, CARD_W - 80, 44,
|
||
g_has_user ? g_username : "(unknown)", 0, fg, card, 0);
|
||
/* Password field */
|
||
fb_text(fb, "Password", card_x + 40, card_y + 210, sub_fg, card);
|
||
draw_rounded_input(fb, card_x + 40, card_y + 240, CARD_W - 80, 44, pass, 1, fg, card, active);
|
||
/* Login button */
|
||
{
|
||
u32 btn_bg = ACCENT;
|
||
fill_rounded_rect(fb, card_x + 40, card_y + 310, 160, 44, 3, btn_bg);
|
||
fb_text(fb, "Login", card_x + 40 + 56, card_y + 310 + (44 - ASCII_H) / 2,
|
||
0xFFFFFFFFu, btn_bg);
|
||
}
|
||
/* Status / error message */
|
||
if (msg && msg[0]) {
|
||
fb_text(fb, msg, card_x + 220, card_y + 322, msg_color, card);
|
||
}
|
||
/* Bottom hint */
|
||
fb_text(fb, "Enter=login Esc=skip Backspace=delete",
|
||
card_x + 40, card_y + CARD_H - 28, DIM_FG, card);
|
||
cursor_save_bg(fb, g_mouse_x, g_mouse_y);
|
||
cursor_draw(fb, g_mouse_x, g_mouse_y);
|
||
}
|
||
|
||
/* Redraw only the password input field (avoids full card redraw on each keystroke) */
|
||
static void redraw_password_only(u32 *fb, i64 card_x, i64 card_y, const char *pass, u32 fg) {
|
||
u32 card = CARD_BG;
|
||
i64 fy = card_y + 240;
|
||
cursor_restore_bg(fb);
|
||
fill_rect(fb, card_x + 40, fy, CARD_W - 80, 44, card);
|
||
draw_rounded_input(fb, card_x + 40, fy, CARD_W - 80, 44, pass, 1, fg, card, 1);
|
||
cursor_save_bg(fb, g_mouse_x, g_mouse_y);
|
||
cursor_draw(fb, g_mouse_x, g_mouse_y);
|
||
}
|
||
|
||
/* ---- verify password against stored hash or encrypted conf ----
|
||
* Returns 1 if password is correct, 0 otherwise.
|
||
* For encrypted conf: XOR-decrypt with SHA256(password); if magic matches, correct.
|
||
* For unencrypted conf: SHA256(password) == stored passwordSha256 hex string.
|
||
*/
|
||
static int verify_password(const u8 *conf_buf, u32 conf_size, const char *password) {
|
||
if (g_is_encrypted) {
|
||
/* Decrypt a copy and check magic */
|
||
u8 hash[32];
|
||
sha256(password, hash);
|
||
static u8 scratch[2048];
|
||
u32 sz = conf_size;
|
||
if (sz > sizeof(scratch)) sz = sizeof(scratch);
|
||
for (u32 i = 0; i < sz; i++) scratch[i] = conf_buf[i] ^ hash[i & 31];
|
||
if (starts_with(scratch, sz, "DESHAB_USERCONF_V1")) {
|
||
/* Also extract username from decrypted buffer for display */
|
||
if (!g_has_user) {
|
||
conf_get_value(scratch, sz, "username", g_username, sizeof(g_username));
|
||
if (g_username[0]) g_has_user = 1;
|
||
}
|
||
return 1;
|
||
}
|
||
return 0;
|
||
} else if (g_has_hash) {
|
||
u8 hash[32];
|
||
sha256(password, hash);
|
||
return hexeq(hash, g_password_hash);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* ---- P8.2 Fix: PS/2 鼠标初始化(login 进入前 mouseInit 可能未运行) ----
|
||
* release 版 is_first=0 路径中 DSK 不加载 mouseInit,login 需自行初始化鼠标
|
||
* 端口轮询方能工作。与 mouseInit 同样的命令序列,但去掉 IRQ12 注册
|
||
* (login 全程 cli,无需中断驱动)。单步失败只记日志,不阻塞登录。 */
|
||
#define PS2_DATA 0x60
|
||
#define PS2_STATUS 0x64
|
||
#define PS2_CMD 0x64
|
||
#define PS2_ST_OBF 0x01
|
||
#define PS2_ST_IBF 0x02
|
||
#define PS2_CMD_READ_CFG 0x20
|
||
#define PS2_CMD_WRITE_CFG 0x60
|
||
#define PS2_CMD_ENABLE_AUX 0xA8
|
||
#define PS2_CMD_TO_MOUSE 0xD4
|
||
#define MOUSE_CMD_RESET 0xFF
|
||
#define MOUSE_CMD_DEFAULTS 0xF6
|
||
#define MOUSE_CMD_RATE 0xF3
|
||
#define MOUSE_CMD_RESOL 0xE8
|
||
#define MOUSE_CMD_ENABLE 0xF4
|
||
#define MOUSE_ACK 0xFA
|
||
#define PS2_TIMEOUT 100000u
|
||
|
||
static int ps2_wait_write(void) {
|
||
for (u32 t = 0; t < PS2_TIMEOUT; t++) {
|
||
if (!(inb(PS2_STATUS) & PS2_ST_IBF)) return 0;
|
||
__asm__ volatile("pause");
|
||
}
|
||
return -1;
|
||
}
|
||
static int ps2_wait_read(void) {
|
||
for (u32 t = 0; t < PS2_TIMEOUT; t++) {
|
||
if (inb(PS2_STATUS) & PS2_ST_OBF) return 0;
|
||
__asm__ volatile("pause");
|
||
}
|
||
return -1;
|
||
}
|
||
static void ps2_drain(void) {
|
||
for (int i = 0; i < 16; i++) {
|
||
if (!(inb(PS2_STATUS) & PS2_ST_OBF)) break;
|
||
inb(PS2_DATA);
|
||
}
|
||
}
|
||
static int ps2_cmd(u8 cmd) {
|
||
if (ps2_wait_write() != 0) return -1;
|
||
outb(PS2_CMD, cmd);
|
||
return 0;
|
||
}
|
||
static int mouse_cmd(u8 cmd) {
|
||
if (ps2_cmd(PS2_CMD_TO_MOUSE) != 0) return -1;
|
||
if (ps2_wait_write() != 0) return -2;
|
||
outb(PS2_DATA, cmd);
|
||
if (ps2_wait_read() != 0) return -3;
|
||
return inb(PS2_DATA) == MOUSE_ACK ? 0 : -4;
|
||
}
|
||
static int mouse_cmd_param(u8 cmd, u8 param) {
|
||
int rc = mouse_cmd(cmd);
|
||
if (rc != 0) return rc;
|
||
if (ps2_cmd(PS2_CMD_TO_MOUSE) != 0) return -5;
|
||
if (ps2_wait_write() != 0) return -6;
|
||
outb(PS2_DATA, param);
|
||
if (ps2_wait_read() != 0) return -7;
|
||
return inb(PS2_DATA) == MOUSE_ACK ? 0 : -8;
|
||
}
|
||
static void login_mouse_init(void) {
|
||
logl("[login] mouse init begin");
|
||
|
||
/* 1. 冲刷残留 */
|
||
ps2_drain();
|
||
|
||
/* 2. 启用 AUX 端口 */
|
||
{
|
||
u8 cfg = 0;
|
||
if (ps2_cmd(PS2_CMD_READ_CFG) == 0 && ps2_wait_read() == 0) {
|
||
cfg = inb(PS2_DATA);
|
||
cfg &= (u8)~0x20u; /* bit5=0: 启用 AUX 时钟 */
|
||
cfg |= 0x02u | 0x40u; /* bit1=AUX IRQ, bit6=翻译 */
|
||
if (ps2_cmd(PS2_CMD_WRITE_CFG) == 0 && ps2_wait_write() == 0) {
|
||
outb(PS2_DATA, cfg);
|
||
logl("[login] cfg byte written");
|
||
} else {
|
||
logl("[login] cfg write failed, continue");
|
||
}
|
||
} else {
|
||
logl("[login] cfg read failed, continue");
|
||
}
|
||
}
|
||
|
||
/* 3. 启用 AUX 端口 */
|
||
if (ps2_cmd(PS2_CMD_ENABLE_AUX) != 0)
|
||
logl("[login] enable AUX timeout, continue");
|
||
|
||
/* 4. 复位鼠标 + drain 应答 */
|
||
{
|
||
int rc = mouse_cmd(MOUSE_CMD_RESET);
|
||
if (rc != 0) logl("[login] reset failed, continue");
|
||
for (int i = 0; i < 8; i++) {
|
||
if (ps2_wait_read() != 0) break;
|
||
inb(PS2_DATA);
|
||
}
|
||
}
|
||
|
||
/* 5-8. Set Defaults -> 采样率 100 -> 分辨率 2 -> 启用数据报告 */
|
||
{
|
||
int rc;
|
||
rc = mouse_cmd(MOUSE_CMD_DEFAULTS);
|
||
if (rc != 0) logl("[login] set defaults failed, continue");
|
||
rc = mouse_cmd_param(MOUSE_CMD_RATE, 100);
|
||
if (rc != 0) logl("[login] sample rate failed, continue");
|
||
rc = mouse_cmd_param(MOUSE_CMD_RESOL, 2);
|
||
if (rc != 0) logl("[login] resolution failed, continue");
|
||
rc = mouse_cmd(MOUSE_CMD_ENABLE);
|
||
if (rc != 0) logl("[login] enable reporting failed, continue");
|
||
else logl("[login] mouse data reporting enabled");
|
||
}
|
||
|
||
logl("[login] mouse init complete");
|
||
}
|
||
|
||
__attribute__((visibility("default")))
|
||
void dsk_entry(const dsk_boot_context *ctx) {
|
||
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
|
||
logl("[login] boot");
|
||
|
||
/* 防篡改:第一时间校验 .text 段完整性。失败 = 系统被篡改,严格 panic。 */
|
||
if (!login_verify_integrity()) {
|
||
login_fatal("LOGIN-E01-INTEGRITY-FAIL");
|
||
}
|
||
|
||
logl("[login] calibrating TSC");
|
||
tsc_calibrate_fi();
|
||
|
||
/* P8.2 Fix: 确保 PS/2 鼠标已初始化(非首次启动时 DSK 不加载 mouseInit) */
|
||
login_mouse_init();
|
||
|
||
if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) {
|
||
login_fatal("LOGIN-E02-BADCONTEXT");
|
||
}
|
||
|
||
fb_a = ctx->framebuffer_address;
|
||
fb_w = ctx->framebuffer_width;
|
||
fb_h = ctx->framebuffer_height;
|
||
fb_p = ctx->framebuffer_pitch;
|
||
|
||
logl("[login] framebuffer ready");
|
||
|
||
/* D 系列:登录界面之前的 ohMyLogo 开场(DEAICUP 莲花) */
|
||
logo_intro_screen((u32 *)(u64)fb_a);
|
||
|
||
/* Get conf data from boot context (passed by DSK) */
|
||
const u8 *conf_buf = (const u8 *)(u64)ctx->reserved[0];
|
||
u32 conf_size = (u32)ctx->reserved[1];
|
||
if (!conf_buf || conf_size == 0) {
|
||
login_fatal("LOGIN-E03-NOUSERCONF");
|
||
}
|
||
logl("[login] conf data received");
|
||
|
||
/* Initialize state */
|
||
g_username[0] = 0;
|
||
g_password_hash[0] = 0;
|
||
g_has_user = 0;
|
||
g_has_hash = 0;
|
||
g_is_encrypted = 0;
|
||
|
||
/* Check if conf is unencrypted (starts with magic) or encrypted */
|
||
if (starts_with(conf_buf, conf_size, "DESHAB_USERCONF_V1")) {
|
||
logl("[login] conf is plaintext, parsing");
|
||
g_is_encrypted = 0;
|
||
if (conf_get_value(conf_buf, conf_size, "username", g_username, sizeof(g_username)) == 0) {
|
||
g_has_user = 1;
|
||
logl("[login] username found");
|
||
} else {
|
||
login_fatal("LOGIN-E04-NO-USERNAME");
|
||
}
|
||
if (conf_get_value(conf_buf, conf_size, "passwordSha256", g_password_hash, sizeof(g_password_hash)) == 0) {
|
||
g_has_hash = 1;
|
||
logl("[login] password hash found");
|
||
} else {
|
||
login_fatal("LOGIN-E05-NO-PASSWORD-HASH");
|
||
}
|
||
} else {
|
||
logl("[login] conf appears encrypted, will verify by password decryption");
|
||
g_is_encrypted = 1;
|
||
/* Username will be extracted after successful decryption */
|
||
}
|
||
|
||
u32 fg = TEXT_FG;
|
||
u32 *fb = (u32 *)(u64)fb_a;
|
||
|
||
/* Fill background */
|
||
fill_gradient_rect(fb, 0, 0, (i64)fb_w, (i64)fb_h);
|
||
|
||
/* Card position (centered) */
|
||
i64 card_x = ((i64)fb_w - CARD_W) / 2;
|
||
i64 card_y = ((i64)fb_h - CARD_H) / 2;
|
||
|
||
/* Initial message */
|
||
const char *msg = "Enter password and press Enter";
|
||
u32 msg_color = DIM_FG;
|
||
|
||
/* Password input buffer */
|
||
char pass[64];
|
||
pass[0] = 0;
|
||
int plen = 0;
|
||
|
||
/* Draw initial UI */
|
||
draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg);
|
||
|
||
/* 初始鼠标光标 */
|
||
cursor_save_bg(fb, g_mouse_x, g_mouse_y);
|
||
cursor_draw(fb, g_mouse_x, g_mouse_y);
|
||
|
||
/* Input loop */
|
||
int shift = 0;
|
||
int release = 0;
|
||
for (;;) {
|
||
/* 鼠标轮询:处理鼠标移动并更新光标 */
|
||
if (ps2_mouse_poll()) {
|
||
cursor_restore_bg(fb);
|
||
cursor_save_bg(fb, g_mouse_x, g_mouse_y);
|
||
cursor_draw(fb, g_mouse_x, g_mouse_y);
|
||
}
|
||
|
||
u8 st = inb(0x64);
|
||
if (!(st & 1)) { __asm__("pause"); continue; }
|
||
/* 鼠标数据留给 ps2_mouse_poll 读取,不消费不丢弃 */
|
||
if (st & 0x20) { __asm__("pause"); continue; }
|
||
u8 data = inb(0x60);
|
||
|
||
u8 sc = data;
|
||
/* Handle extended (E0) sequences — ignore (no arrow keys needed for login) */
|
||
if (sc == 0xE0) { continue; }
|
||
/* Handle release (F0 in Set 2, or high bit in Set 1) */
|
||
if (sc == 0xF0) { release = 1; continue; }
|
||
if (release) { release = 0; /* track shift release below */ }
|
||
|
||
/* Shift press/release */
|
||
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
|
||
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
|
||
|
||
/* Ignore high-bit (release in Set 1) */
|
||
if (sc & 0x80) continue;
|
||
|
||
/* Escape (sc 0x01) — skip login */
|
||
if (sc == 0x01) {
|
||
logl("[login] Esc pressed, skipping login");
|
||
((dsk_boot_context *)ctx)->reserved[2] = 0;
|
||
return;
|
||
}
|
||
|
||
char c = scan_to_ascii(sc, shift);
|
||
if (!c) continue;
|
||
|
||
if (c == '\n') {
|
||
/* Submit password */
|
||
if (plen == 0) {
|
||
/* Empty password — show hint, don't verify */
|
||
msg = "Password is empty";
|
||
msg_color = ERROR_FG;
|
||
draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg);
|
||
continue;
|
||
}
|
||
logl("[login] verifying password");
|
||
int ok = verify_password(conf_buf, conf_size, pass);
|
||
if (ok) {
|
||
logl("[login] password correct");
|
||
((dsk_boot_context *)ctx)->reserved[2] = 1;
|
||
/* Show success message briefly */
|
||
draw_login_card(fb, card_x, card_y, pass, 0,
|
||
"Login successful", 0xFF5FAF6Fu, fg);
|
||
for (int i = 0; i < 30; i++) delay_frame();
|
||
return;
|
||
} else {
|
||
logl("[login] password wrong");
|
||
msg = "Wrong password, try again";
|
||
msg_color = ERROR_FG;
|
||
/* Clear password */
|
||
pass[0] = 0;
|
||
plen = 0;
|
||
draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (c == 8) {
|
||
/* Backspace */
|
||
if (plen > 0) {
|
||
pass[--plen] = 0;
|
||
redraw_password_only(fb, card_x, card_y, pass, fg);
|
||
}
|
||
continue;
|
||
}
|
||
|
||
/* Regular character */
|
||
if (plen < 62 && c >= 32 && c <= 126) {
|
||
pass[plen++] = c;
|
||
pass[plen] = 0;
|
||
redraw_password_only(fb, card_x, card_y, pass, fg);
|
||
}
|
||
}
|
||
}
|