feat: Phase 1 网络端到端打通 + FirstInit/Login/DevMode 完善

- 用户态共享网络协议栈 CODE/tools/net_stack.h(eth/ARP/IPv4/ICMP/UDP-DNS/TCP static inline,轮询模型)
- shell 内建 ping/curl/run 命令;ping.elf/curl.elf 对齐 net_stack.h 真实协议栈
- shell dev_mode 串口镜像(g_serial_mirror)便于自动化验证
- DSK 启动流程:firstInit.txt 双行格式(firstInit + dev_mode),dev_mode=1 触发 shell 自动回归测试
- FirstInit 完成→持久化 USER.CONF + 翻转 firstInit 0->1(保留 dev_mode)
- firstInit=1 时 DSK 加载 login.elf 进行用户选择 + 密码校验后再进 desktop
- 新增 CODE/login/ 登录应用、CODE/DKM/dkm_shared.h、Logo.png
- ahci/e1000/virtio_net/fat32/console_fb 驱动更新;utsm serial/block/dsk_loader 调整
- build.ps1 打包 ping.elf/curl.elf 进 SATA FAT32 根目录
- 移除 CODE/tools/bash(与 shell.elf 重复)
This commit is contained in:
xyou
2026-07-26 21:07:36 +08:00
parent 48dcb5568e
commit efa626ed17
83 changed files with 4993 additions and 1785 deletions
-25
View File
@@ -1,25 +0,0 @@
CC := clang
LD := ld.lld
ROOT := ../../..
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/bash.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
-16
View File
@@ -1,16 +0,0 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
-456
View File
@@ -1,456 +0,0 @@
/* Deshab Bash — 全屏终端应用
* PS/2 键盘输入,命令行编辑,内置命令。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
#define TERM_ROWS 40
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
#define CHAR_STEP 12
#define CHAR_H 18
#define PROMPT_STR "deshab# "
static da_app_context g_ac;
/* 终端缓冲区 */
static u8 term_ch[TERM_MAX_CHARS];
static u32 term_fg[TERM_MAX_CHARS];
static int term_w = TERM_COLS;
static int term_h = TERM_ROWS;
static int cur_col = 0;
static int cur_row = 0;
/* 输入缓冲 */
static char input_buf[256];
static int input_len = 0;
static int input_cursor = 0;
/* 命令历史 */
#define HIST_SIZE 16
static char history[HIST_SIZE][256];
static int hist_count = 0;
static int hist_idx = 0;
/* 坐标 */
static int term_x, term_y;
static int content_h;
static void term_init_layout(void) {
term_x = 8;
term_y = DA_TITLEBAR_H + 4;
int avail_w = (int)g_ac.fb_w - 16;
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
term_w = avail_w / CHAR_STEP;
term_h = avail_h / CHAR_H;
if (term_w < 10) term_w = 10;
if (term_h < 5) term_h = 5;
if (term_w > TERM_COLS) term_w = TERM_COLS;
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
content_h = term_h * CHAR_H;
}
static void term_clear(void) {
for (int i = 0; i < TERM_MAX_CHARS; i++) {
term_ch[i] = ' ';
term_fg[i] = DA_TEXT_PRIMARY;
}
cur_col = 0; cur_row = 0;
}
static void term_scroll(void) {
for (int r = 1; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
}
}
for (int c = 0; c < term_w; c++) {
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
}
cur_row = term_h - 1; cur_col = 0;
}
static void term_newline(void) {
cur_col = 0; cur_row++;
if (cur_row >= term_h) term_scroll();
}
static void term_putc_color(char c, u32 color) {
if (c == '\n') { term_newline(); return; }
if (c == '\r') { cur_col = 0; return; }
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
if (c < ' ' || c > '~') c = '?';
if (cur_col >= term_w) term_newline();
int idx = cur_row * TERM_COLS + cur_col;
term_ch[idx] = (u8)c; term_fg[idx] = color;
cur_col++;
}
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
/* ---- 绘制 ---- */
static void redraw_all(void) {
da_fill_bg(&g_ac, DA_BG_PRIMARY);
da_draw_titlebar(&g_ac, "Deshab Bash", (i64)g_ac.fb_w);
/* 终端字符 */
for (int r = 0; r < term_h; r++) {
for (int c = 0; c < term_w; c++) {
int idx = r * TERM_COLS + c;
if (term_ch[idx] == ' ') continue;
da_draw_char(&g_ac, term_ch[idx],
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
term_fg[idx], DA_BG_PRIMARY);
}
}
/* 光标 */
if (cur_col < term_w && cur_row < term_h) {
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* 状态栏 */
char status[64];
int p = 0;
const char *s1 = "/ root@deshab ";
while (*s1) status[p++] = *s1++;
u8 hh, mm; da_rtc_time(&hh, &mm);
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
status[p] = 0;
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* ---- 内置命令 ---- */
static int str_eq(const char *a, const char *b) {
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
return *a == *b;
}
static void cmd_help(void) {
term_puts_color("Deshab Bash - 内置命令:\n", DA_PROMPT_COLOR);
term_puts(" help 显示此帮助\n");
term_puts(" clear 清屏\n");
term_puts(" echo <text> 显示文本\n");
term_puts(" version 显示系统版本\n");
term_puts(" ver 同 version\n");
term_puts(" uname 显示系统名称\n");
term_puts(" date 显示当前日期时间\n");
term_puts(" about 关于 Deshab\n");
term_puts(" reboot 重启系统\n");
term_puts(" halt 关机\n");
term_puts(" pwd 显示当前目录\n");
term_puts(" whoami 显示当前用户\n");
term_puts(" id 显示用户ID\n");
term_puts(" ls 列出根目录文件\n");
term_puts(" cat <file> 显示文件内容\n");
}
static void cmd_version(void) {
term_puts_color("Deshab OS v0.1.0\n", DA_SUCCESS);
term_puts(" Kernel: DSK (Deshab System Kernel)\n");
term_puts(" Architecture: x86_64\n");
term_puts(" ABI: DKM v1\n");
term_puts(" Bootloader: Limine\n");
}
static void cmd_uname(void) { term_puts("Deshab\n"); }
static void cmd_about(void) {
term_puts_color("=== Deshab OS ===\n", DA_PROMPT_COLOR);
term_puts("单地址空间 Ring0 内核实验系统\n");
term_puts("架构: UTSM + DSK + DKM\n");
term_puts_color("Built with Clang + lld\n", DA_TEXT_DIM);
}
static void cmd_clear(void) { term_clear(); }
static void cmd_reboot(void) {
term_puts_color("Rebooting...\n", DA_PROMPT_COLOR);
redraw_all();
outb(0x64, 0xFE);
for(;;) __asm__("hlt");
}
static void cmd_halt(void) {
term_puts_color("System halted.\n", DA_PROMPT_COLOR);
redraw_all();
for(;;) __asm__("hlt");
}
static void cmd_echo(const char *args) {
if (args && *args) term_puts_color(args, DA_TEXT_PRIMARY);
term_putc('\n');
}
static void cmd_date(void) {
u8 year, month, day, hour, minute, second;
outb(0x70, 9); year = inb(0x71);
outb(0x70, 8); month = inb(0x71);
outb(0x70, 7); day = inb(0x71);
outb(0x70, 4); hour = inb(0x71);
outb(0x70, 2); minute = inb(0x71);
outb(0x70, 0); second = inb(0x71);
year = (u8)((year>>4)*10+(year&0xF));
month = (u8)((month>>4)*10+(month&0xF));
day = (u8)((day>>4)*10+(day&0xF));
hour = (u8)((hour>>4)*10+(hour&0xF));
minute = (u8)((minute>>4)*10+(minute&0xF));
second = (u8)((second>>4)*10+(second&0xF));
char buf[32]; int p = 0;
buf[p++]='2'; buf[p++]='0'; buf[p++]='0'+year/10; buf[p++]='0'+year%10; buf[p++]='-';
buf[p++]='0'+month/10; buf[p++]='0'+month%10; buf[p++]='-';
buf[p++]='0'+day/10; buf[p++]='0'+day%10; buf[p++]=' ';
buf[p++]='0'+hour/10; buf[p++]='0'+hour%10; buf[p++]=':';
buf[p++]='0'+minute/10; buf[p++]='0'+minute%10; buf[p++]=':';
buf[p++]='0'+second/10; buf[p++]='0'+second%10; buf[p]=0;
term_puts(buf); term_putc('\n');
}
static void cmd_ls(void) {
if (!g_ac.block_read) {
term_puts_color("ls: 无块设备可用\n", DA_ERROR);
return;
}
/* 读取 FAT32 根目录 — 简化版:读取 BPB 获取根目录区 */
u8 bpb[512];
if (g_ac.block_read(0, 0, 1, bpb) != 0) {
term_puts_color("ls: 读取 BPB 失败\n", DA_ERROR);
return;
}
/* 检查 FAT32 签名 */
if (bpb[510] != 0x55 || bpb[511] != 0xAA) {
term_puts_color("ls: 无有效 FAT32 分区\n", DA_ERROR);
return;
}
u32 root_cluster = *(u32 *)(bpb + 44);
u16 bytes_per_sector = *(u16 *)(bpb + 11);
u8 sectors_per_cluster = bpb[13];
u32 fat_start = *(u16 *)(bpb + 14);
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2;
(void)bytes_per_sector;
(void)sectors_per_cluster;
(void)data_start;
/* 读取根目录簇 — 简化:只读取第一个簇 */
u8 dir_buf[8192];
u32 cluster = root_cluster;
u32 lba = data_start + (cluster - 2) * sectors_per_cluster;
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) {
term_puts_color("ls: 读取根目录失败\n", DA_ERROR);
return;
}
/* 解析目录项 */
int found = 0;
for (int i = 0; i < (int)(sectors_per_cluster * 512); i += 32) {
u8 first = dir_buf[i];
if (first == 0x00) break;
if (first == 0xE5) continue;
if (dir_buf[i+11] & 0x08) continue; /* 卷标 */
if (dir_buf[i+11] & 0x10) {
/* 目录 */
term_puts_color("[DIR] ", DA_ACCENT);
} else {
term_puts_color(" ", DA_TEXT_PRIMARY);
}
/* 文件名(8.3) */
for (int j = 0; j < 8; j++) {
if (dir_buf[i+j] == ' ') break;
term_putc((char)dir_buf[i+j]);
}
if (dir_buf[i+8] != ' ') {
term_putc('.');
for (int j = 8; j < 11; j++) {
if (dir_buf[i+j] == ' ') break;
term_putc((char)dir_buf[i+j]);
}
}
term_putc('\n');
found++;
}
if (found == 0) term_puts_color("(empty)\n", DA_TEXT_DIM);
}
static void cmd_cat(const char *args) {
if (!args || !*args) {
term_puts_color("cat: 需要文件名参数\n", DA_ERROR);
return;
}
if (!g_ac.block_read) {
term_puts_color("cat: 无块设备可用\n", DA_ERROR);
return;
}
term_puts_color("cat: 文件读取暂未完整实现\n", DA_WARNING);
}
static void execute_command(const char *cmd) {
while (*cmd == ' ') cmd++;
if (*cmd == 0) return;
const char *args = cmd;
while (*args && *args != ' ') args++;
char name[32];
int nlen = (int)(args - cmd);
if (nlen >= 32) nlen = 31;
for (int i = 0; i < nlen; i++) name[i] = cmd[i];
name[nlen] = 0;
while (*args == ' ') args++;
if (str_eq(name,"help")||str_eq(name,"?")) cmd_help();
else if (str_eq(name,"version")||str_eq(name,"ver")) cmd_version();
else if (str_eq(name,"uname")) cmd_uname();
else if (str_eq(name,"about")) cmd_about();
else if (str_eq(name,"clear")||str_eq(name,"cls")) cmd_clear();
else if (str_eq(name,"reboot")) cmd_reboot();
else if (str_eq(name,"halt")||str_eq(name,"shutdown")) cmd_halt();
else if (str_eq(name,"echo")) cmd_echo(args);
else if (str_eq(name,"date")||str_eq(name,"time")) cmd_date();
else if (str_eq(name,"ls")||str_eq(name,"dir")) cmd_ls();
else if (str_eq(name,"cat")) cmd_cat(args);
else if (str_eq(name,"pwd")) { term_puts("/\n"); }
else if (str_eq(name,"whoami")) { term_puts("root\n"); }
else if (str_eq(name,"id")) { term_puts("uid=0(root) gid=0(root)\n"); }
else {
term_puts_color("未知命令: ", DA_ERROR);
term_puts_color(name, DA_ERROR);
term_putc('\n');
term_puts_color("输入 'help' 查看可用命令\n", DA_TEXT_DIM);
}
}
static void add_history(const char *cmd) {
if (hist_count < HIST_SIZE) {
int i;
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
history[hist_count][i] = 0;
hist_count++;
}
hist_idx = hist_count;
}
/* ---- 输入行重绘 ---- */
static void redraw_input_line(void) {
int plen = 0;
while (PROMPT_STR[plen]) plen++;
i64 y = term_y + (i64)cur_row * CHAR_H;
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
for (int i = 0; i < plen; i++)
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
for (int i = 0; i < input_len; i++)
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
cur_col = plen + input_cursor;
/* 光标 */
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
}
/* ---- 主入口 ---- */
__attribute__((visibility("default")))
void dsk_entry(const da_boot_context *ctx) {
__asm__ volatile("cli");
da_slog("bash", "boot");
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
da_slog("bash", "bad context");
for(;;) __asm__("hlt");
}
da_init(&g_ac, ctx);
term_init_layout();
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab Bash v0.1 ===\n", DA_ACCENT);
term_puts_color("欢迎使用 Deshab 终端\n", DA_SUCCESS);
term_puts_color("输入 'help' 查看可用命令 | Esc 退出\n", DA_TEXT_DIM);
term_putc('\n');
redraw_all();
int shift = 0, e0 = 0;
for (;;) {
/* 绘制提示符 */
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
input_len = 0; input_cursor = 0; input_buf[0] = 0;
int plen = 0; while (PROMPT_STR[plen]) plen++;
cur_col = plen;
redraw_all();
int cmd_done = 0;
while (!cmd_done) {
u8 st = inb(0x64);
if (!(st & 1)) { __asm__("pause"); continue; }
u8 data = inb(0x60);
if (st & 0x20) continue;
u8 sc = data;
if (sc == 0xE0) { e0 = 1; continue; }
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
if (sc & 0x80) { e0 = 0; continue; }
/* Esc 退出 */
if (sc == 0x01 && !e0) {
da_slog("bash", "exit");
return;
}
if (e0) {
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
else if (sc == 0x47) { input_cursor = 0; }
else if (sc == 0x4F) { input_cursor = input_len; }
else if (sc == 0x48) { /* 上箭头 - 历史 */
if (hist_idx > 0) {
hist_idx--;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
}
}
else if (sc == 0x50) { /* 下箭头 - 历史 */
if (hist_idx < hist_count - 1) {
hist_idx++;
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
input_len = i; input_cursor = i; input_buf[i] = 0;
} else if (hist_idx < hist_count) {
hist_idx = hist_count;
input_len = 0; input_cursor = 0; input_buf[0] = 0;
}
}
e0 = 0;
redraw_input_line();
continue;
}
if (sc == 0x1C) { /* Enter */
input_buf[input_len] = 0;
term_putc('\n');
if (input_len > 0) add_history(input_buf);
execute_command(input_buf);
cmd_done = 1;
continue;
}
if (sc == 0x0E) { /* Backspace */
if (input_cursor > 0) {
for (int i = input_cursor - 1; i < input_len - 1; i++)
input_buf[i] = input_buf[i+1];
input_len--; input_cursor--;
input_buf[input_len] = 0;
}
redraw_input_line();
continue;
}
char c = da_scan_to_ascii(sc, shift);
if (c && c >= 32 && c <= 126 && input_len < 255) {
for (int i = input_len; i > input_cursor; i--)
input_buf[i] = input_buf[i-1];
input_buf[input_cursor++] = c;
input_len++;
redraw_input_line();
}
}
redraw_all();
}
}
+12 -4
View File
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
dynamic PT_DYNAMIC FLAGS(4);
}
SECTIONS
@@ -12,5 +13,12 @@ SECTIONS
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
.data : ALIGN(4K) {
*(.data*)
*(.data.rel.ro*)
*(COMMON)
*(.bss*)
} :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
}
+12 -4
View File
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
dynamic PT_DYNAMIC FLAGS(4);
}
SECTIONS
@@ -12,5 +13,12 @@ SECTIONS
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
.data : ALIGN(4K) {
*(.data*)
*(.data.rel.ro*)
*(COMMON)
*(.bss*)
} :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
}
+112 -21
View File
@@ -1,11 +1,11 @@
/* Deshab curl — 终端风格 HTTP 客户端 stub
* 命令行输入 URL,显示 HTTP GET 请求结果。
* 当前为 stub,提示需要 TCP/IP 栈支持。
/* Deshab curl — 终端风格 HTTP 客户端
* 命令行输入 URL,发送 HTTP/1.0 GET 请求并打印响应(net_stack.h 共享协议栈)。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#include "../net_stack.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
@@ -125,29 +125,121 @@ static void redraw_all(void) {
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
}
/* ---- URL 请求模拟 ---- */
/* ---- HTTP GET 实现(net_stack.h) ---- */
static int g_ns_status = 0; /* 0=未尝试, 1=就绪, <0=失败 */
static int curl_net_ready(void) {
if (g_ns_status == 1) return 1;
if (g_ns_status < 0) return 0;
int rc = ns_init((u64)g_ac.kernel_api);
g_ns_status = (rc == 0) ? 1 : -1;
if (rc != 0) {
term_puts_color(" [错误] 网络初始化失败,无可用网卡或 net API\n", DA_ERROR);
return 0;
}
return 1;
}
static void do_http_get(const char *url) {
if (!curl_net_ready()) return;
const char *p = url;
if (p[0]=='h'&&p[1]=='t'&&p[2]=='t'&&p[3]=='p'&&p[4]==':'&&p[5]=='/'&&p[6]=='/') p += 7;
char host[128]; int hl = 0;
while (*p && *p != ':' && *p != '/' && hl < 127) host[hl++] = *p++;
host[hl] = 0;
if (!hl) { term_puts_color(" [错误] 缺少主机名\n", DA_ERROR); return; }
u16 port = 80;
if (*p == ':') {
p++;
u32 pv = 0; int digits = 0;
while (*p >= '0' && *p <= '9') { pv = pv * 10 + (u32)(*p - '0'); p++; digits++; }
if (digits && pv <= 65535) port = (u16)pv;
}
char path[128]; int pl = 0;
if (*p == '/') {
while (*p && *p != ' ' && pl < 127) path[pl++] = *p++;
}
if (!pl) { path[0] = '/'; pl = 1; }
path[pl] = 0;
term_puts_color("GET ", DA_PROMPT_COLOR);
term_puts_color(url, DA_ACCENT_LIGHT);
term_putc('\n');
term_puts_color(" -> 解析主机...\n", DA_TEXT_DIM);
term_puts_color(" -> 建立 TCP 连接...\n", DA_TEXT_DIM);
/* stub: 检查 kernel_api 是否有 net 接口 */
if (!g_ac.kernel_api) {
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
/* 解析主机 */
term_puts_color(" -> 解析 ", DA_TEXT_DIM);
term_puts_color(host, DA_TEXT_DIM);
term_puts_color("...\n", DA_TEXT_DIM);
redraw_all();
u32 ip;
if (ns_dns_resolve(host, &ip) != 0) {
term_puts_color(" [错误] DNS 解析失败\n", DA_ERROR);
return;
}
/* 尝试读取 net API — 当前为 stub */
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
if (!net_api) {
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
term_puts_color(" 当前内核未实现网络协议栈。\n", DA_WARNING);
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
char ipstr[24]; ns_fmt_ip(ip, ipstr);
term_puts_color(" -> ", DA_TEXT_DIM);
term_puts_color(ipstr, DA_TEXT_DIM);
term_putc('\n');
/* TCP 连接 */
term_puts_color(" -> TCP 连接...\n", DA_TEXT_DIM);
redraw_all();
if (ns_tcp_connect(ip, port, 2000) != 0) {
term_puts_color(" [错误] TCP 连接失败(超时/RST)\n", DA_ERROR);
return;
}
/* 如果有 net API,此处应实现真正的 HTTP GET */
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
/* 构造 HTTP/1.0 请求 */
static char req[512];
int r = 0;
const char *m = "GET "; while (*m) req[r++] = *m++;
for (int i = 0; i < pl; i++) req[r++] = path[i];
m = " HTTP/1.0\r\nHost: "; while (*m) req[r++] = *m++;
for (int i = 0; i < hl; i++) req[r++] = host[i];
m = "\r\nConnection: close\r\n\r\n"; while (*m) req[r++] = *m++;
req[r] = 0;
term_puts_color(" -> 已连接,发送请求\n", DA_TEXT_DIM);
redraw_all();
if (ns_tcp_send((const u8 *)req, (u32)r) != 0) {
term_puts_color(" [错误] 发送失败\n", DA_ERROR);
ns_tcp_close();
return;
}
/* 收至 FIN */
term_puts_color(" -> 等待响应...\n", DA_TEXT_DIM);
redraw_all();
int n = ns_tcp_recv(0, 0, 5000);
ns_tcp_close();
if (n < 0) {
term_puts_color(" [错误] 连接被重置\n", DA_ERROR);
return;
}
if (n == 0) {
term_puts_color(" [错误] 无响应数据(超时)\n", DA_ERROR);
return;
}
term_puts_color(" -> 收到 ", DA_SUCCESS);
char nb[12]; ns_u32_dec(nb, (u32)n);
term_puts_color(nb, DA_SUCCESS);
term_puts_color(" 字节\n\n", DA_SUCCESS);
/* 打印 status line + body */
u32 total = ns_tcp_rx_len;
u32 show = (u32)n;
for (u32 i = 0; i < show; i++) term_putc((char)ns_tcp_rx[i]);
if (total > show) {
term_puts_color("\n[... 已截断,共 ", DA_TEXT_DIM);
ns_u32_dec(nb, total);
term_puts_color(nb, DA_TEXT_DIM);
term_puts_color(" 字节]\n", DA_TEXT_DIM);
}
term_putc('\n');
}
/* ---- 内置命令 ---- */
@@ -168,8 +260,8 @@ static void cmd_help(void) {
static void cmd_clear(void) { term_clear(); }
static void cmd_version(void) {
term_puts_color("Deshab curl v0.1 (stub)\n", DA_SUCCESS);
term_puts_color(" HTTP 客户端功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
term_puts_color("Deshab curl v1.0\n", DA_SUCCESS);
term_puts_color(" HTTP/1.0 GET via net_stack (e1000 + slirp)\n", DA_TEXT_DIM);
}
static void execute_command(const char *cmd) {
@@ -231,10 +323,9 @@ void dsk_entry(const da_boot_context *ctx) {
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab curl v0.1 (stub) ===\n", DA_ACCENT);
term_puts_color("=== Deshab curl v1.0 ===\n", DA_ACCENT);
term_puts_color("HTTP 命令行客户端\n", DA_SUCCESS);
term_puts_color("输入 URL 发送 GET 请求 | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
term_putc('\n');
redraw_all();
+12 -4
View File
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
dynamic PT_DYNAMIC FLAGS(4);
}
SECTIONS
@@ -12,5 +13,12 @@ SECTIONS
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
.data : ALIGN(4K) {
*(.data*)
*(.data.rel.ro*)
*(COMMON)
*(.bss*)
} :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
}
+12 -4
View File
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
dynamic PT_DYNAMIC FLAGS(4);
}
SECTIONS
@@ -12,5 +13,12 @@ SECTIONS
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
.data : ALIGN(4K) {
*(.data*)
*(.data.rel.ro*)
*(COMMON)
*(.bss*)
} :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
}
+716
View File
@@ -0,0 +1,716 @@
/* net_stack.h — Deshab 用户态共享网络协议栈(static inline,无库)
*
* 前置条件:包含者已定义 u8/u16/u32/u64/i64 基础类型。
* 通过 dkm_kernel_api + 0x48 的 net API 收发原始以太帧(轮询模型,无 IRQ)。
*
* 能力:eth II / ARP(含应答)/ IPv4 / ICMP echo / UDP DNS / TCP 最小 client。
* 默认拓扑:QEMU slirp — guest 10.0.2.15 / gw 10.0.2.2 / dns 10.0.2.3。
*
* 用法:
* ns_init(ctx->dkm_kernel_api) — 初始化,选第一个有线 tx/rx ready 设备
* ns_ping(ip, seq, timeout, &rtt, &ttl) — 单次 ICMP echo
* ns_dns_resolve(host, &ip) — A 记录查询(数字 IP 直接返回)
* ns_tcp_connect(ip, port, timeout)
* ns_tcp_send(data, len)
* ns_tcp_recv(buf, cap, idle_timeout) — 收至 FIN/超时,返回累计长度
* ns_tcp_close()
*
* IP 以 u32 网络序数值表示(NS_IP4(10,0,2,15))。
*/
#ifndef DESHAB_NET_STACK_H
#define DESHAB_NET_STACK_H
/* ---- 默认网络配置(QEMU user-mode slirp) ---- */
#define NS_IP4(a,b,c,d) ((u32)((((u32)(a))<<24)|(((u32)(b))<<16)|(((u32)(c))<<8)|((u32)(d))))
#define NS_GUEST_IP NS_IP4(10,0,2,15)
#define NS_GATEWAY_IP NS_IP4(10,0,2,2)
#define NS_DNS_IP NS_IP4(10,0,2,3)
#define NS_NETMASK NS_IP4(255,255,255,0)
#define NS_ETH_ARP 0x0806
#define NS_ETH_IPV4 0x0800
#define NS_IP_ICMP 1
#define NS_IP_TCP 6
#define NS_IP_UDP 17
#define NS_ARP_CACHE_SIZE 8
#define NS_TCP_RX_CAP 65536
/* dkm_net_device_desc.flags 位(与 utsm/net.h 一致) */
#define NS_NET_F_TX_READY 0x2
#define NS_NET_F_RX_READY 0x4
#define NS_NET_F_WIRELESS 0x8
/* ---- 端口 I/O / TSC(ns_ 前缀避免与包含者冲突) ---- */
static inline void ns_outb(u16 p, u8 v) { __asm__ volatile("outb %0,%1"::"a"(v),"Nd"(p)); }
static inline u8 ns_inb(u16 p) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(p)); return v; }
static inline u64 ns_rdtsc(void) {
u32 lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
/* PIT ch0 ~10ms 校准 TSC(与 shell/dsk 同法) */
static u64 ns_tsc_per_ms = 0;
static inline void ns_tsc_calibrate(void) {
if (ns_tsc_per_ms) return;
ns_outb(0x43, 0x30);
ns_outb(0x40, 0x7C);
ns_outb(0x40, 0x2E);
u64 t0 = ns_rdtsc();
u16 prev = 0; u64 loops = 0;
for (;;) {
ns_outb(0x43, 0x00);
u16 cur = (u16)ns_inb(0x40) | ((u16)ns_inb(0x40) << 8);
if (cur > prev && loops > 10) break;
prev = cur; loops++;
}
ns_tsc_per_ms = (ns_rdtsc() - t0) / 10;
if (!ns_tsc_per_ms) ns_tsc_per_ms = 3000; /* 兜底 ~3GHz */
}
static inline int ns_deadline_reached(u64 deadline) {
return (i64)(ns_rdtsc() - deadline) >= 0;
}
/* ---- 字节序/校验和 ---- */
static inline void ns_w16(u8 *p, u16 v) { p[0] = (u8)(v >> 8); p[1] = (u8)v; }
static inline void ns_w32(u8 *p, u32 v) { p[0] = (u8)(v >> 24); p[1] = (u8)(v >> 16); p[2] = (u8)(v >> 8); p[3] = (u8)v; }
static inline u16 ns_r16(const u8 *p) { return (u16)(((u16)p[0] << 8) | p[1]); }
static inline u32 ns_r32(const u8 *p) { return ((u32)p[0] << 24) | ((u32)p[1] << 16) | ((u32)p[2] << 8) | p[3]; }
static inline u16 ns_checksum(const void *data, u32 len) {
const u8 *p = (const u8 *)data;
u32 sum = 0;
while (len > 1) { sum += ((u16)p[0] << 8) | p[1]; p += 2; len -= 2; }
if (len) sum += ((u16)p[0] << 8);
while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
return (u16)~sum;
}
static inline void ns_memcpy(void *d, const void *s, u32 n) {
u8 *dd = (u8 *)d; const u8 *ss = (const u8 *)s;
while (n--) *dd++ = *ss++;
}
static inline void ns_memset(void *d, u8 c, u32 n) {
u8 *dd = (u8 *)d; while (n--) *dd++ = c;
}
/* ---- 十进制/IP 字符串 ---- */
static inline void ns_u32_dec(char *buf, u32 v) {
char tmp[12]; int n = 0;
if (!v) { buf[0] = '0'; buf[1] = 0; return; }
while (v && n < 11) { tmp[n++] = (char)('0' + v % 10); v /= 10; }
for (int i = 0; i < n; i++) buf[i] = tmp[n - 1 - i];
buf[n] = 0;
}
static inline void ns_fmt_ip(u32 ip, char *out) {
int p = 0;
for (int i = 3; i >= 0; i--) {
char d[4]; ns_u32_dec(d, (ip >> (i * 8)) & 0xFF);
for (int j = 0; d[j]; j++) out[p++] = d[j];
if (i) out[p++] = '.';
}
out[p] = 0;
}
/* 解析 "a.b.c.d" → 网络序 u32。返回 0 成功。 */
static inline int ns_parse_ip(const char *s, u32 *out) {
u32 v = 0;
for (int part = 0; part < 4; part++) {
u32 n = 0; int digits = 0;
while (*s >= '0' && *s <= '9') {
n = n * 10 + (u32)(*s - '0');
if (n > 255) return -1;
s++; digits++;
}
if (!digits) return -1;
v = (v << 8) | n;
if (part < 3) {
if (*s != '.') return -1;
s++;
}
}
if (*s && *s != ' ') return -1;
*out = v;
return 0;
}
/* ---- net API 绑定 ---- */
typedef int (*ns_net_tx_fn)(u32 index, const void *packet, u32 length);
typedef int (*ns_net_rx_poll_fn)(u32 index, void *buffer, u32 capacity, u32 *out_length);
typedef u32 (*ns_net_count_fn)(void);
typedef int (*ns_net_info_fn)(u32 index, void *out);
static int ns_ready = 0;
static u32 ns_dev_index = 0;
static u8 ns_mac[6] = {0};
static ns_net_tx_fn ns_net_tx = 0;
static ns_net_rx_poll_fn ns_net_rx_poll_fn_ptr = 0;
static u16 ns_ip_id = 1;
/* dkm_net_device_info 布局:name ptr @0, mac[6] @8, flags @16(对齐填充后 size 24) */
typedef struct __attribute__((packed)) {
u64 name;
u8 mac[6];
u16 pad;
u32 flags;
} ns_dev_info;
static inline int ns_init(u64 dkm_kernel_api) {
if (ns_ready) return 0;
if (!dkm_kernel_api) return -1;
u64 net = *(u64 *)(dkm_kernel_api + 0x48);
if (!net) return -2;
ns_net_count_fn device_count = (ns_net_count_fn)*(u64 *)(net + 0x08);
ns_net_info_fn device_info = (ns_net_info_fn)*(u64 *)(net + 0x10);
ns_net_tx = (ns_net_tx_fn)*(u64 *)(net + 0x18);
ns_net_rx_poll_fn_ptr = (ns_net_rx_poll_fn)*(u64 *)(net + 0x20);
if (!device_count || !device_info || !ns_net_tx || !ns_net_rx_poll_fn_ptr) return -3;
u32 n = device_count();
if (!n) return -4;
int picked = -1;
ns_dev_info info;
for (u32 i = 0; i < n && i < 8; i++) {
ns_memset(&info, 0, sizeof(info));
if (device_info(i, &info) != 0) continue;
if (info.flags & NS_NET_F_WIRELESS) continue;
if ((info.flags & NS_NET_F_TX_READY) && (info.flags & NS_NET_F_RX_READY)) {
picked = (int)i;
for (int k = 0; k < 6; k++) ns_mac[k] = info.mac[k];
break;
}
}
if (picked < 0) {
ns_memset(&info, 0, sizeof(info));
if (device_info(0, &info) != 0) return -5;
picked = 0;
for (int k = 0; k < 6; k++) ns_mac[k] = info.mac[k];
}
ns_dev_index = (u32)picked;
ns_tsc_calibrate();
ns_ready = 1;
return 0;
}
/* ---- 帧缓冲 ---- */
static u8 ns_txbuf[2048];
static u8 ns_rxframe[2048];
/* 发送以太帧 */
static inline int ns_send_eth(const u8 dst_mac[6], u16 ethertype, const u8 *payload, u32 len) {
if (len + 14 > sizeof(ns_txbuf)) return -1;
for (int i = 0; i < 6; i++) ns_txbuf[i] = dst_mac[i];
for (int i = 0; i < 6; i++) ns_txbuf[6 + i] = ns_mac[i];
ns_w16(ns_txbuf + 12, ethertype);
ns_memcpy(ns_txbuf + 14, payload, len);
u32 total = len + 14;
if (total < 60) { /* 以太网最小帧 */
ns_memset(ns_txbuf + total, 0, 60 - total);
total = 60;
}
return ns_net_tx(ns_dev_index, ns_txbuf, total);
}
/* ---- ARP ---- */
typedef struct {
u32 ip;
u8 mac[6];
u8 valid;
} ns_arp_entry;
static ns_arp_entry ns_arp_cache[NS_ARP_CACHE_SIZE];
static u8 ns_arp_reply_mac[6];
static volatile int ns_arp_reply_valid = 0;
static inline void ns_arp_cache_put(u32 ip, const u8 mac[6]) {
int slot = -1;
for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) {
if (ns_arp_cache[i].valid && ns_arp_cache[i].ip == ip) { slot = i; break; }
}
if (slot < 0) {
for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) {
if (!ns_arp_cache[i].valid) { slot = i; break; }
}
}
if (slot < 0) slot = 0;
ns_arp_cache[slot].ip = ip;
for (int k = 0; k < 6; k++) ns_arp_cache[slot].mac[k] = mac[k];
ns_arp_cache[slot].valid = 1;
}
static inline int ns_arp_cache_get(u32 ip, u8 mac[6]) {
for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) {
if (ns_arp_cache[i].valid && ns_arp_cache[i].ip == ip) {
for (int k = 0; k < 6; k++) mac[k] = ns_arp_cache[i].mac[k];
return 0;
}
}
return -1;
}
/* 构造 ARP 包到 out(42 字节),oper 1=request 2=reply */
static inline void ns_arp_build(u8 *out, u16 oper,
const u8 sha[6], u32 spa,
const u8 tha[6], u32 tpa) {
ns_w16(out + 0, 1); /* htype eth */
ns_w16(out + 2, NS_ETH_IPV4); /* ptype */
out[4] = 6; out[5] = 4;
ns_w16(out + 6, oper);
for (int i = 0; i < 6; i++) out[8 + i] = sha[i];
ns_w32(out + 14, spa);
for (int i = 0; i < 6; i++) out[18 + i] = tha[i];
ns_w32(out + 24, tpa);
}
/* 处理收到的 ARP 包:应答对我们的请求,记录 reply。 */
static inline void ns_arp_handle(const u8 *pkt, u32 len) {
if (len < 28) return;
u16 oper = ns_r16(pkt + 6);
u32 spa = ns_r32(pkt + 14);
u32 tpa = ns_r32(pkt + 24);
if (oper == 1 && tpa == NS_GUEST_IP) {
/* 请求我们的 IP → 回复 */
u8 reply[28];
ns_arp_build(reply, 2, ns_mac, NS_GUEST_IP, pkt + 8, spa);
ns_send_eth(pkt + 8, NS_ETH_ARP, reply, 28);
} else if (oper == 2) {
ns_arp_cache_put(spa, pkt + 8);
for (int k = 0; k < 6; k++) ns_arp_reply_mac[k] = pkt[8 + k];
ns_arp_reply_valid = 1;
}
}
/* 轮询一帧;ARP 内部处理(不返回给调用者)。
* 返回 0=收到非 ARP 帧(*out_et 为 ethertype),1=超时。 */
static inline int ns_wait_frame(u8 *buf, u32 cap, u32 *out_len, u16 *out_et, u32 timeout_ms) {
u64 deadline = ns_rdtsc() + ns_tsc_per_ms * timeout_ms;
for (;;) {
u32 len = 0;
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, buf, cap, &len);
if (rc == 0 && len >= 14) {
u16 et = ns_r16(buf + 12);
if (et == NS_ETH_ARP) {
ns_arp_handle(buf + 14, len - 14);
/* 继续等待 */
} else {
*out_len = len;
*out_et = et;
return 0;
}
}
if (ns_deadline_reached(deadline)) return 1;
__asm__ volatile("pause");
}
}
/* ARP 解析 ip → mac,3 次重传。返回 0 成功。 */
static inline int ns_arp_resolve(u32 ip, u8 out_mac[6]) {
if (ns_arp_cache_get(ip, out_mac) == 0) return 0;
static const u8 bcast[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
static const u8 zero_mac[6] = {0,0,0,0,0,0};
for (int attempt = 0; attempt < 3; attempt++) {
u8 req[28];
ns_arp_build(req, 1, ns_mac, NS_GUEST_IP, zero_mac, ip);
ns_arp_reply_valid = 0;
if (ns_send_eth(bcast, NS_ETH_ARP, req, 28) != 0) continue;
u64 deadline = ns_rdtsc() + ns_tsc_per_ms * 1000;
while (!ns_deadline_reached(deadline)) {
u32 len = 0;
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len);
if (rc == 0 && len >= 14 && ns_r16(ns_rxframe + 12) == NS_ETH_ARP) {
ns_arp_handle(ns_rxframe + 14, len - 14);
if (ns_arp_reply_valid) {
u32 spa = ns_r32(ns_rxframe + 14 + 14);
if (spa == ip) {
for (int k = 0; k < 6; k++) out_mac[k] = ns_arp_reply_mac[k];
return 0;
}
ns_arp_reply_valid = 0;
}
}
__asm__ volatile("pause");
}
}
return -1;
}
/* 目标 IP 的下一跳 MAC(同子网直连,否则走网关)。返回 0 成功。 */
static inline int ns_route_mac(u32 dst_ip, u8 out_mac[6]) {
u32 next = dst_ip;
if ((dst_ip & NS_NETMASK) != (NS_GUEST_IP & NS_NETMASK)) next = NS_GATEWAY_IP;
return ns_arp_resolve(next, out_mac);
}
/* ---- IPv4 发送 ---- */
static inline void ns_ipv4_build(u8 *out, u8 proto, u32 src, u32 dst, u32 payload_len) {
out[0] = 0x45; /* ver=4 ihl=5 */
out[1] = 0; /* tos */
ns_w16(out + 2, (u16)(20 + payload_len));
ns_w16(out + 4, ns_ip_id++);
ns_w16(out + 6, 0x4000); /* DF */
out[8] = 64; /* ttl */
out[9] = proto;
ns_w16(out + 10, 0);
ns_w32(out + 12, src);
ns_w32(out + 16, dst);
ns_w16(out + 10, ns_checksum(out, 20));
}
/* ---- ICMP echo(ping) ----
* 返回 0 成功(*out_rtt_ms / *out_ttl 有效),-1 ARP 失败,-2 超时。 */
static inline int ns_ping(u32 dst_ip, u16 seq, u32 timeout_ms, u32 *out_rtt_ms, u8 *out_ttl) {
u8 dst_mac[6];
if (ns_route_mac(dst_ip, dst_mac) != 0) return -1;
u8 pkt[20 + 8 + 32];
ns_ipv4_build(pkt, NS_IP_ICMP, NS_GUEST_IP, dst_ip, 8 + 32);
u8 *icmp = pkt + 20;
icmp[0] = 8; /* echo request */
icmp[1] = 0;
ns_w16(icmp + 2, 0);
ns_w16(icmp + 4, 0xD5AB); /* identifier */
ns_w16(icmp + 6, seq);
for (int i = 0; i < 32; i++) icmp[8 + i] = (u8)(0x61 + (i % 26));
ns_w16(icmp + 2, ns_checksum(icmp, 8 + 32));
if (ns_send_eth(dst_mac, NS_ETH_IPV4, pkt, sizeof(pkt)) != 0) return -3;
u64 t0 = ns_rdtsc();
u64 deadline = t0 + ns_tsc_per_ms * timeout_ms;
while (!ns_deadline_reached(deadline)) {
u32 len = 0;
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len);
if (rc == 0 && len >= 14) {
u16 et = ns_r16(ns_rxframe + 12);
if (et == NS_ETH_ARP) {
ns_arp_handle(ns_rxframe + 14, len - 14);
} else if (et == NS_ETH_IPV4 && len >= 14 + 20 + 8) {
const u8 *ip = ns_rxframe + 14;
u8 ihl = (u8)((ip[0] & 0x0F) * 4);
if (ihl < 20 || len < 14 + ihl + 8) continue;
if (ip[9] != NS_IP_ICMP) continue;
const u8 *ic = ip + ihl;
if (ic[0] == 0 && ns_r16(ic + 4) == 0xD5AB && ns_r16(ic + 6) == seq) {
u64 dt = ns_rdtsc() - t0;
if (out_rtt_ms) {
u32 ms = (u32)(dt / ns_tsc_per_ms);
*out_rtt_ms = ms;
}
if (out_ttl) *out_ttl = ip[8];
return 0;
}
}
}
__asm__ volatile("pause");
}
return -2;
}
/* ---- UDP DNS A 查询 ----
* host 为数字 IP 时直接解析返回。返回 0 成功。 */
static inline int ns_dns_resolve(const char *host, u32 *out_ip) {
if (ns_parse_ip(host, out_ip) == 0) return 0;
u8 dst_mac[6];
if (ns_route_mac(NS_DNS_IP, dst_mac) != 0) return -1;
/* 构造查询:header + qname + qtype/qclass */
u8 q[512];
u16 txid = (u16)(ns_rdtsc() & 0xFFFF);
if (!txid) txid = 0x1234;
ns_w16(q + 0, txid);
ns_w16(q + 2, 0x0100); /* RD */
ns_w16(q + 4, 1); /* qdcount */
ns_w16(q + 6, 0); ns_w16(q + 8, 0); ns_w16(q + 10, 0);
u32 p = 12;
u32 i = 0;
while (host[i]) {
u32 start = p++;
u8 n = 0;
while (host[i] && host[i] != '.') {
if (n >= 63 || p >= 500) return -2;
q[p++] = (u8)host[i++]; n++;
}
q[start] = n;
if (host[i] == '.') i++;
}
q[p++] = 0;
ns_w16(q + p, 1); p += 2; /* A */
ns_w16(q + p, 1); p += 2; /* IN */
u32 qlen = p;
u8 pkt[20 + 8 + 512];
ns_ipv4_build(pkt, NS_IP_UDP, NS_GUEST_IP, NS_DNS_IP, 8 + qlen);
u8 *udp = pkt + 20;
ns_w16(udp + 0, 49153);
ns_w16(udp + 2, 53);
ns_w16(udp + 4, (u16)(8 + qlen));
ns_w16(udp + 6, 0); /* 不校验 UDP checksum */
ns_memcpy(udp + 8, q, qlen);
for (int attempt = 0; attempt < 2; attempt++) {
if (ns_send_eth(dst_mac, NS_ETH_IPV4, pkt, 20 + 8 + qlen) != 0) continue;
u64 deadline = ns_rdtsc() + ns_tsc_per_ms * 3000;
while (!ns_deadline_reached(deadline)) {
u32 len = 0;
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len);
if (rc == 0 && len >= 14) {
u16 et = ns_r16(ns_rxframe + 12);
if (et == NS_ETH_ARP) {
ns_arp_handle(ns_rxframe + 14, len - 14);
} else if (et == NS_ETH_IPV4 && len >= 14 + 20 + 8 + 12) {
const u8 *ip = ns_rxframe + 14;
u8 ihl = (u8)((ip[0] & 0x0F) * 4);
if (ihl < 20 || ip[9] != NS_IP_UDP) continue;
const u8 *ru = ip + ihl;
if (ns_r16(ru + 2) != 49153) continue;
const u8 *d = ru + 8;
if (ns_r16(d + 0) != txid) continue;
u16 qd = ns_r16(d + 4);
u16 an = ns_r16(d + 6);
u32 off = 12;
/* 跳过问题段 */
for (u16 qq = 0; qq < qd; qq++) {
while (off < len) {
u8 b = d[off];
if (b == 0) { off++; break; }
if ((b & 0xC0) == 0xC0) { off += 2; break; }
off += 1 + b;
}
off += 4;
}
/* 遍历应答 RR */
for (u16 rr = 0; rr < an; rr++) {
while (off < len) { /* 跳过 name */
u8 b = d[off];
if (b == 0) { off++; break; }
if ((b & 0xC0) == 0xC0) { off += 2; break; }
off += 1 + b;
}
if (off + 10 > len) break;
u16 rtype = ns_r16(d + off);
u16 rdlen = ns_r16(d + off + 8);
off += 10;
if (rtype == 1 && rdlen == 4 && off + 4 <= len) {
*out_ip = ns_r32(d + off);
return 0;
}
off += rdlen;
}
}
}
__asm__ volatile("pause");
}
}
return -3;
}
/* ---- TCP 最小 client ---- */
#define NS_TCP_CLOSED 0
#define NS_TCP_SYN_SENT 1
#define NS_TCP_ESTAB 2
#define NS_TCP_FIN_WAIT 3
static struct {
u32 state;
u32 dst_ip;
u16 dst_port;
u16 src_port;
u32 snd_nxt;
u32 rcv_nxt;
u8 dst_mac[6];
} ns_tc = {0, 0, 0, 0, 0, 0, {0}};
static u8 ns_tcp_rx[NS_TCP_RX_CAP];
static u32 ns_tcp_rx_len = 0;
static volatile int ns_tcp_got_fin = 0;
static volatile int ns_tcp_got_rst = 0;
#define NS_TCP_FIN 0x01
#define NS_TCP_SYN 0x02
#define NS_TCP_RST 0x04
#define NS_TCP_PSH 0x08
#define NS_TCP_ACK 0x10
/* 构造并发送 TCP 段(data 可为 0)。 */
static inline int ns_tcp_emit(u8 flags, const u8 *data, u32 len, u32 seq) {
u8 pkt[20 + 20 + 1460];
if (len > 1460) return -1;
u32 tcp_len = 20 + len;
ns_ipv4_build(pkt, NS_IP_TCP, NS_GUEST_IP, ns_tc.dst_ip, tcp_len);
u8 *t = pkt + 20;
ns_w16(t + 0, ns_tc.src_port);
ns_w16(t + 2, ns_tc.dst_port);
ns_w32(t + 4, seq);
ns_w32(t + 8, (flags & NS_TCP_ACK) ? ns_tc.rcv_nxt : 0);
t[12] = (5 << 4); /* data offset */
t[13] = flags;
ns_w16(t + 14, 64240); /* window */
ns_w16(t + 16, 0);
ns_w16(t + 18, 0);
if (len) ns_memcpy(t + 20, data, len);
/* 伪头部校验和 */
u8 ph[12];
ns_w32(ph + 0, NS_GUEST_IP);
ns_w32(ph + 4, ns_tc.dst_ip);
ph[8] = 0; ph[9] = NS_IP_TCP;
ns_w16(ph + 10, (u16)tcp_len);
u32 sum = 0;
for (u32 k = 0; k < 12; k += 2) sum += ((u16)ph[k] << 8) | ph[k + 1];
for (u32 k = 0; k + 1 < tcp_len; k += 2) sum += ((u16)t[k] << 8) | t[k + 1];
if (tcp_len & 1) sum += ((u16)t[tcp_len - 1] << 8);
while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
ns_w16(t + 16, (u16)~sum);
return ns_send_eth(ns_tc.dst_mac, NS_ETH_IPV4, pkt, 20 + tcp_len);
}
/* 处理收到的 IPv4/TCP 段。返回 1 表示属于本连接并已处理。 */
static inline int ns_tcp_process(const u8 *ip, u32 ip_len) {
if (ip_len < 20) return 0;
u8 ihl = (u8)((ip[0] & 0x0F) * 4);
if (ihl < 20 || ip_len < ihl + 20) return 0;
if (ip[9] != NS_IP_TCP) return 0;
if (ns_r32(ip + 12) != ns_tc.dst_ip) return 0;
const u8 *t = ip + ihl;
if (ns_r16(t + 0) != ns_tc.dst_port) return 0;
if (ns_r16(t + 2) != ns_tc.src_port) return 0;
u32 seq = ns_r32(t + 4);
u32 ack = ns_r32(t + 8);
u8 doff = (u8)((t[12] >> 4) * 4);
u8 flags = t[13];
u16 ip_total = ns_r16(ip + 2);
u32 data_len = (u32)ip_total - ihl - doff;
const u8 *data = t + doff;
if (flags & NS_TCP_RST) { ns_tcp_got_rst = 1; ns_tc.state = NS_TCP_CLOSED; return 1; }
if (ns_tc.state == NS_TCP_SYN_SENT) {
if ((flags & (NS_TCP_SYN | NS_TCP_ACK)) == (NS_TCP_SYN | NS_TCP_ACK)) {
ns_tc.rcv_nxt = seq + 1;
ns_tc.snd_nxt = ack;
ns_tc.state = NS_TCP_ESTAB;
ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt);
}
return 1;
}
if (ns_tc.state == NS_TCP_ESTAB || ns_tc.state == NS_TCP_FIN_WAIT) {
if (data_len > 0) {
if (seq == ns_tc.rcv_nxt) {
u32 room = NS_TCP_RX_CAP - ns_tcp_rx_len;
u32 take = data_len < room ? data_len : room;
ns_memcpy(ns_tcp_rx + ns_tcp_rx_len, data, take);
ns_tcp_rx_len += take;
ns_tc.rcv_nxt += data_len;
ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt);
} else {
/* 乱序超前或重复段:重 ACK 当前期望序号 */
ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt);
}
}
if (flags & NS_TCP_FIN) {
if (seq + data_len == ns_tc.rcv_nxt || data_len == 0) {
ns_tc.rcv_nxt = seq + data_len + 1;
ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt);
ns_tcp_got_fin = 1;
ns_tc.state = NS_TCP_CLOSED;
}
}
return 1;
}
return 1;
}
/* 在超时窗口内收帧并喂给 TCP;返回 0=收到本连接段,1=超时。 */
static inline int ns_tcp_wait(u32 timeout_ms) {
u64 deadline = ns_rdtsc() + ns_tsc_per_ms * timeout_ms;
while (!ns_deadline_reached(deadline)) {
u32 len = 0;
int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len);
if (rc == 0 && len >= 14) {
u16 et = ns_r16(ns_rxframe + 12);
if (et == NS_ETH_ARP) {
ns_arp_handle(ns_rxframe + 14, len - 14);
} else if (et == NS_ETH_IPV4) {
if (ns_tcp_process(ns_rxframe + 14, len - 14)) return 0;
}
}
__asm__ volatile("pause");
}
return 1;
}
/* 返回 0 成功(state=ESTAB),-1 ARP 失败,-2 超时/RST。 */
static inline int ns_tcp_connect(u32 dst_ip, u16 dst_port, u32 timeout_ms) {
if (ns_route_mac(dst_ip, ns_tc.dst_mac) != 0) return -1;
ns_tc.dst_ip = dst_ip;
ns_tc.dst_port = dst_port;
ns_tc.src_port = (u16)(49152 + (ns_rdtsc() & 0x0FFF));
u32 iss = (u32)ns_rdtsc();
ns_tcp_rx_len = 0;
ns_tcp_got_fin = 0;
ns_tcp_got_rst = 0;
for (int attempt = 0; attempt < 3; attempt++) {
ns_tc.state = NS_TCP_SYN_SENT;
if (ns_tcp_emit(NS_TCP_SYN, 0, 0, iss) != 0) continue;
u64 deadline = ns_rdtsc() + ns_tsc_per_ms * timeout_ms;
while (!ns_deadline_reached(deadline)) {
if (ns_tcp_got_rst) { ns_tc.state = NS_TCP_CLOSED; return -2; }
if (ns_tc.state == NS_TCP_ESTAB) return 0;
ns_tcp_wait(50);
}
}
ns_tc.state = NS_TCP_CLOSED;
return -2;
}
/* 发送数据并等待 ACK(数据段可能在等待期间到达,已入缓冲)。 */
static inline int ns_tcp_send(const u8 *data, u32 len) {
if (ns_tc.state != NS_TCP_ESTAB) return -1;
for (int attempt = 0; attempt < 3; attempt++) {
u32 expect = ns_tc.snd_nxt + len;
if (ns_tcp_emit((u8)(NS_TCP_PSH | NS_TCP_ACK), data, len, ns_tc.snd_nxt) != 0) return -2;
u64 deadline = ns_rdtsc() + ns_tsc_per_ms * 2000;
while (!ns_deadline_reached(deadline)) {
if (ns_tcp_got_rst) { ns_tc.state = NS_TCP_CLOSED; return -3; }
/* 检查 snd_una 是否被 ACK 推进:ns_tcp_process 不记录 una,
* 简单起见此处认为发出后收到任何本连接段即推进。
* 对 HTTP/1.0 请求-响应模式足够(对端 ACK+数据一起回来)。 */
if (ns_tcp_wait(100) == 0) {
ns_tc.snd_nxt = expect;
return 0;
}
}
}
return -4;
}
/* 收至 FIN 或空闲超时;把累计数据拷到 buf(最多 cap)。返回长度或负值。 */
static inline int ns_tcp_recv(u8 *buf, u32 cap, u32 idle_timeout_ms) {
u64 idle_deadline = ns_rdtsc() + ns_tsc_per_ms * idle_timeout_ms;
for (;;) {
if (ns_tcp_got_rst) return -1;
if (ns_tcp_got_fin) break;
if (ns_deadline_reached(idle_deadline)) break;
if (ns_tcp_wait(100) == 0) {
idle_deadline = ns_rdtsc() + ns_tsc_per_ms * idle_timeout_ms;
}
}
u32 n = ns_tcp_rx_len < cap ? ns_tcp_rx_len : cap;
if (buf && n) ns_memcpy(buf, ns_tcp_rx, n);
return (int)n;
}
static inline void ns_tcp_close(void) {
if (ns_tc.state == NS_TCP_ESTAB) {
ns_tcp_emit((u8)(NS_TCP_FIN | NS_TCP_ACK), 0, 0, ns_tc.snd_nxt);
}
ns_tc.state = NS_TCP_CLOSED;
}
#endif /* DESHAB_NET_STACK_H */
+12 -4
View File
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(7);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(7);
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
dynamic PT_DYNAMIC FLAGS(4);
}
SECTIONS
@@ -12,5 +13,12 @@ SECTIONS
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
.data : ALIGN(4K) {
*(.data*)
*(.data.rel.ro*)
*(COMMON)
*(.bss*)
} :data
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
}
+80 -29
View File
@@ -1,11 +1,11 @@
/* Deshab ping — 终端风格 ICMP Echo 工具 stub
* 命令行输入 IP 地址,发送 ICMP Echo Request。
* 当前为 stub,提示需要 TCP/IP 栈支持。
/* Deshab ping — 终端风格 ICMP Echo 工具
* 命令行输入 IP 地址,发送 ICMP Echo Request(net_stack.h 共享协议栈)。
* Esc 键退出返回 DSK。
*/
#include "../../firstInit/ascii_bitmaps.c"
#include "../desktop_app.h"
#include "../net_stack.h"
/* ---- 终端参数 ---- */
#define TERM_COLS 120
@@ -137,32 +137,84 @@ static int is_valid_ip(const char *s) {
return dots == 3;
}
/* ---- Ping 模拟 ---- */
static void do_ping(const char *ip) {
/* ---- Ping 实现(net_stack.h) ---- */
static int g_ns_status = 0; /* 0=未尝试, 1=就绪, <0=失败 */
static int ping_net_ready(void) {
if (g_ns_status == 1) return 1;
if (g_ns_status < 0) return 0;
int rc = ns_init((u64)g_ac.kernel_api);
g_ns_status = (rc == 0) ? 1 : -1;
if (rc != 0) {
term_puts_color(" [错误] 网络初始化失败,无可用网卡或 net API\n", DA_ERROR);
return 0;
}
return 1;
}
static void do_ping(const char *ip_str) {
u32 ip;
if (ns_parse_ip(ip_str, &ip) != 0) {
term_puts_color(" [错误] 无法解析 IP\n", DA_ERROR);
return;
}
if (!ping_net_ready()) return;
char ipstr[24];
ns_fmt_ip(ip, ipstr);
term_puts_color("PING ", DA_PROMPT_COLOR);
term_puts_color(ip, DA_ACCENT_LIGHT);
term_puts_color(" 56(84) bytes of data.\n", DA_TEXT_DIM);
term_puts_color(ipstr, DA_ACCENT_LIGHT);
term_puts_color(" 32 data bytes\n", DA_TEXT_DIM);
/* stub: 检查 kernel_api 是否有 net 接口 */
if (!g_ac.kernel_api) {
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
return;
int sent = 0, recv = 0;
u32 sum = 0, min = 0xFFFFFFFFu, max = 0;
for (u16 seq = 1; seq <= 4; seq++) {
u32 rtt = 0; u8 ttl = 0;
int rc = ns_ping(ip, seq, 2000, &rtt, &ttl);
sent++;
if (rc == 0) {
recv++;
sum += rtt;
if (rtt < min) min = rtt;
if (rtt > max) max = rtt;
term_puts("reply from ");
term_puts(ipstr);
term_puts(": bytes=32 time=");
char nb[12]; ns_u32_dec(nb, rtt);
term_puts(nb);
term_puts("ms TTL=");
ns_u32_dec(nb, ttl);
term_puts(nb);
term_putc('\n');
} else if (rc == -1) {
term_puts_color("ARP 解析失败,目标不可达\n", DA_ERROR);
break;
} else {
term_puts_color("request timed out\n", DA_TEXT_DIM);
}
redraw_all();
}
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
if (!net_api) {
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
term_puts_color(" 当前内核未实现 ICMP 协议栈。\n", DA_WARNING);
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
term_putc('\n');
term_puts_color("--- ", DA_TEXT_DIM);
term_puts_color(ip, DA_TEXT_DIM);
term_puts_color(" ping statistics ---\n", DA_TEXT_DIM);
term_puts_color(" 0 packets transmitted, 0 received, 100% packet loss\n", DA_ERROR);
return;
char nb[12];
ns_u32_dec(nb, (u32)sent);
term_puts_color("--- ", DA_TEXT_DIM);
term_puts_color(ipstr, DA_TEXT_DIM);
term_puts_color(" ping statistics ---\n ", DA_TEXT_DIM);
term_puts(nb);
term_puts(" transmitted, ");
ns_u32_dec(nb, (u32)recv);
term_puts(nb);
term_puts(" received, ");
u32 loss = sent ? (u32)(sent - recv) * 100 / (u32)sent : 100;
ns_u32_dec(nb, loss);
term_puts(nb);
term_puts("% loss");
if (recv > 0) {
term_puts(", min/avg/max = ");
ns_u32_dec(nb, min); term_puts(nb); term_puts("/");
ns_u32_dec(nb, sum / (u32)recv); term_puts(nb); term_puts("/");
ns_u32_dec(nb, max); term_puts(nb); term_puts(" ms");
}
/* 如果有 net API,此处应实现真正的 ICMP Echo */
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
term_putc('\n');
}
/* ---- 内置命令 ---- */
@@ -183,8 +235,8 @@ static void cmd_help(void) {
static void cmd_clear(void) { term_clear(); }
static void cmd_version(void) {
term_puts_color("Deshab ping v0.1 (stub)\n", DA_SUCCESS);
term_puts_color(" ICMP Echo 功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
term_puts_color("Deshab ping v1.0\n", DA_SUCCESS);
term_puts_color(" ICMP Echo via net_stack (e1000 + slirp)\n", DA_TEXT_DIM);
}
static void execute_command(const char *cmd) {
@@ -246,10 +298,9 @@ void dsk_entry(const da_boot_context *ctx) {
term_clear();
/* 启动 banner */
term_puts_color("=== Deshab ping v0.1 (stub) ===\n", DA_ACCENT);
term_puts_color("=== Deshab ping v1.0 ===\n", DA_ACCENT);
term_puts_color("ICMP Echo 请求工具\n", DA_SUCCESS);
term_puts_color("输入 IP 地址发送 ping | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
term_putc('\n');
redraw_all();