VulpesOS/tundra/boot/usb-init.c
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

132 lines
5.3 KiB
C

/* SPDX-License-Identifier: MPL-2.0
* Read-only, development-only USB diagnostics over Linux FunctionFS.
* No shell, block-device access, reboot command or arbitrary file service.
*/
#define _start stage0_start
#include "init.c"
#undef _start
#if defined(__x86_64__)
#define NR_mkdirat 258
#define NR_symlinkat 266
#define NR_getdents64 217
#else
#define NR_mkdirat 34
#define NR_symlinkat 36
#define NR_getdents64 61
#endif
#define G "/sys/kernel/config/usb_gadget/tundra"
typedef unsigned int u32;
static long length(const char *s) { long n=0; while(s[n]) ++n; return n; }
static void pause_ms(long ms) {
long t[2]={ms/1000,(ms%1000)*1000000}; call(NR_nanosleep,(long)t,0,0,0,0);
}
static void fail(const char *s) {
say("TUNDRA_USB_FAILED ");say(s);say("\n");
for (;;) pause_ms(10000);
}
static void directory(const char *p) {
long r=call(NR_mkdirat,-100,(long)p,0755,0,0);
if(r<0 && r!=-17) fail(p);
}
static void put(const char *p,const char *value) {
long fd=call(NR_openat,-100,(long)p,1,0,0);
if(fd<0) fail(p);
long n=length(value);
if(call(NR_write,fd,(long)value,n,0,0)!=n) fail(p);
call(NR_close,fd,0,0,0,0);
}
static void send(long fd,const char *value) {
long n=length(value);
while(n) {
long r=call(NR_write,fd,(long)value,n,0,0);
if(r<=0) return;
n-=r;value+=r;
}
}
static int equal(const char *a,const char *b) {
while(*a && *b && *a==*b) {++a;++b;}return *a==*b;
}
static const unsigned char descriptors[]={
/* v2 header: magic, length, FS+HS+SS flags and descriptor counts */
3,0,0,0, 105,0,0,0, 7,0,0,0, 3,0,0,0, 3,0,0,0, 5,0,0,0,
9,4,0,0,2,255,0,0,1, 7,5,0x81,2,64,0,0, 7,5,0x02,2,64,0,0,
9,4,0,0,2,255,0,0,1, 7,5,0x81,2,0,2,0, 7,5,0x02,2,0,2,0,
9,4,0,0,2,255,0,0,1,
7,5,0x81,2,0,4,0, 6,48,0,0,0,0,
7,5,0x02,2,0,4,0, 6,48,0,0,0,0
};
static const struct __attribute__((packed)) {
u32 magic,length,count,languages;
unsigned short language;
char name[18];
} strings={2,36,1,1,0x0409,"Tundra diagnostic"};
static void bind_udc(void) {
char data[4096];
for(int retry=0;retry<300;retry++) {
long fd=call(NR_openat,-100,(long)"/sys/class/udc",0,0,0);
if(fd>=0) {
long n=call(NR_getdents64,fd,(long)data,sizeof(data),0,0);
call(NR_close,fd,0,0,0,0);
for(long i=0;i+19<n;) {
unsigned short size=*(unsigned short *)(data+i+16);
if(size<20 || i+size>n) break;
char *name=data+i+19;
if(name[0]!='.') {put(G "/UDC",name);say("TUNDRA_USB_BOUND ");say(name);say("\n");return;}
i+=size;
}
}
pause_ms(100);
}
fail("no USB device controller");
}
__attribute__((noreturn)) void _start(void) {
if(call(NR_getpid,0,0,0,0,0)!=1) {
say("USB diagnostic must be PID 1 in its diagnostic environment.\n");
call(NR_exit,2,0,0,0,0);for(;;){}
}
call(NR_mount,(long)"proc",(long)"/proc",(long)"proc",14,0);
char buffer[4096];file("/proc/cmdline",buffer,sizeof(buffer));
if(!contains(buffer,"tundra.usb=1")) fail("explicit kernel flag required");
call(NR_mount,(long)"sysfs",(long)"/sys",(long)"sysfs",14,0);
/* Sargo's stock kernel has no devtmpfs. FunctionFS creates its own nodes. */
if(call(NR_mount,(long)"tmpfs",(long)"/dev",(long)"tmpfs",2,0)<0) fail("dev tmpfs");
directory("/sys/kernel/config");
if(call(NR_mount,(long)"configfs",(long)"/sys/kernel/config",(long)"configfs",14,0)<0) fail("configfs");
directory(G);
/* Linux development gadget IDs, not an assigned Vulpes product identity. */
put(G "/idVendor","0x1d6b");put(G "/idProduct","0x0104");
put(G "/bcdUSB","0x0300");
directory(G "/strings/0x409");
put(G "/strings/0x409/manufacturer","Tundra development");
put(G "/strings/0x409/product","Read-only boot diagnostic");
put(G "/strings/0x409/serialnumber","TUNDRA-DIAG");
directory(G "/configs/c.1");put(G "/configs/c.1/MaxPower","250");
directory(G "/functions/ffs.tundra");directory("/dev/ffs-tundra");
if(call(NR_mount,(long)"tundra",(long)"/dev/ffs-tundra",(long)"functionfs",0,0)<0) fail("FunctionFS mount");
long ep0=call(NR_openat,-100,(long)"/dev/ffs-tundra/ep0",2|2048,0,0);
if(ep0<0 || call(NR_write,ep0,(long)descriptors,sizeof(descriptors),0,0)!=sizeof(descriptors)) fail("descriptors");
if(call(NR_write,ep0,(long)&strings,sizeof(strings),0,0)!=sizeof(strings)) fail("strings");
if(call(NR_symlinkat,(long)G "/functions/ffs.tundra",-100,(long)G "/configs/c.1/diagnostic",0,0)<0) fail("function link");
bind_udc();
long input=call(NR_openat,-100,(long)"/dev/ffs-tundra/ep2",2048,0,0);
long output=call(NR_openat,-100,(long)"/dev/ffs-tundra/ep1",1,0,0);
if(input<0 || output<0) fail("endpoints");
say("TUNDRA_USB_READY protocol=1 commands=status,version,mounts\n");
for(;;) {
/* Drain bind/enable events. Unsupported control requests remain stalled. */
char events[96];call(NR_read,ep0,(long)events,sizeof(events),0,0);
long n=call(NR_read,input,(long)buffer,63,0,0);
if(n>0) {
buffer[n]=0;
const char *path=0;
if(equal(buffer,"status\n")) send(output,"TUNDRA_USB_STATUS protocol=1 readOnly=true\nEND\n");
else {
if(equal(buffer,"version\n")) path="/proc/version";
if(equal(buffer,"mounts\n")) path="/proc/mounts";
if(path) {file(path,buffer,sizeof(buffer));send(output,buffer);send(output,"\nEND\n");}
else send(output,"ERROR unsupported command\nEND\n");
}
}
pause_ms(20);
}
}