1# 2# ipaq 3# 4# TO DO: read params from params flash 5# 6 7implement Init; 8 9include "sys.m"; 10 sys: Sys; 11 12include "draw.m"; 13 14include "keyring.m"; 15 kr: Keyring; 16 17include "security.m"; 18 auth: Auth; 19 20include "dhcp.m"; 21 dhcpclient: Dhcpclient; 22 Bootconf: import dhcpclient; 23 24include "keyboard.m"; 25 26include "sh.m"; 27 28Init: module 29{ 30 init: fn(); 31}; 32 33Bootpreadlen: con 128; 34 35ethername := "ether0"; 36 37# standard Inferno flash partitions 38 39flashparts := array[] of { 40 # bootstrap at 0x0 to 0x40000, don't touch 41 "add params 0x40000 0x80000", 42 "add kernel 0x80000 0x140000", 43 "add fs 0x140000 end", 44}; 45 46# 47# initialise flash translation 48# mount flash file system 49# add devices 50# start a shell or window manager 51# 52 53init() 54{ 55 sys = load Sys Sys->PATH; 56 kr = load Keyring Keyring->PATH; 57 auth = load Auth Auth->PATH; 58 if(auth != nil) 59 auth->init(); 60 61 sys->bind("/", "/", Sys->MREPL); 62 63 lightup(); 64 65 localok := 0; 66 if(lfs() >= 0){ 67 # let's just take a closer look 68 sys->bind("/n/local/nvfs", "/nvfs", Sys->MREPL|Sys->MCREATE); 69 (rc, nil) := sys->stat("/n/local/dis/sh.dis"); 70 if(rc >= 0) 71 localok = 1; 72 else 73 err("local file system unusable"); 74 } 75 netok := sys->bind("#l", "/net", Sys->MREPL) >= 0; 76 if(!netok){ 77 netok = sys->bind("#l1", "/net", Sys->MREPL) >= 0; 78 if(netok) 79 ethername = "ether1"; 80 } 81 if(netok) 82 configether(); 83 84 dobind("#I", "/net", sys->MAFTER); # IP 85 dobind("#p", "/prog", sys->MREPL); # prog 86 dobind("#c", "/dev", sys->MREPL); # console 87 sys->bind("#d", "/fd", Sys->MREPL); 88 dobind("#t", "/dev", sys->MAFTER); # serial line 89 dobind("#i", "/dev", sys->MAFTER); # draw 90 dobind("#m", "/dev", Sys->MAFTER); # pointer 91 sys->bind("#e", "/env", sys->MREPL|sys->MCREATE); # environment 92 sys->bind("#A", "/dev", Sys->MAFTER); # optional audio 93 dobind("#T","/dev",sys->MAFTER); # touch screen and other ipaq devices 94 95 timefile: string; 96 rootsource: string; 97 cfd := sys->open("/dev/consctl", Sys->OWRITE); 98 if(cfd != nil) 99 sys->fprint(cfd, "rawon"); 100 for(;;){ 101 (rootsource, timefile) = askrootsource(localok, netok); 102 if(rootsource == nil) 103 break; # internal 104 (rc, nil) := sys->stat(rootsource+"/dis/sh.dis"); 105 if(rc < 0) 106 err("%s has no shell"); 107 else if(sys->bind(rootsource, "/", Sys->MAFTER) < 0) 108 sys->print("can't bind %s on /: %r\n", rootsource); 109 else{ 110 sys->bind(rootsource+"/dis", "/dis", Sys->MBEFORE|Sys->MCREATE); 111 break; 112 } 113 } 114 cfd = nil; 115 116 setsysname("ipaq"); # set system name 117 118 now := getclock(timefile, rootsource); 119 setclock("/dev/time", now); 120 if(timefile != "#r/rtc") 121 setclock("#r/rtc", now/big 1000000); 122 123 sys->chdir("/"); 124 if(netok){ 125 start("ndb/dns", nil); 126 start("ndb/cs", nil); 127 } 128 calibrate(); 129 startup := "/nvfs/startup"; 130 if(sys->open(startup, Sys->OREAD) != nil){ 131 shell := load Command Sh->PATH; 132 if(shell != nil){ 133 sys->print("Running %s\n", startup); 134 shell->init(nil, "sh" :: startup :: nil); 135 } 136 } 137 user := rdenv("user", "inferno"); 138 (ok, nil) := sys->stat("/dis/wm/wm.dis"); 139 if(ok >= 0) 140 (ok, nil) = sys->stat("/dis/wm/logon.dis"); 141 if(ok >= 0 && userok(user)){ 142 wm := load Command "/dis/wm/wm.dis"; 143 if(wm != nil){ 144 fd := sys->open("/nvfs/user", Sys->OWRITE); 145 if(fd != nil){ 146 sys->fprint(fd, "%s", user); 147 fd = nil; 148 } 149 spawn wm->init(nil, list of {"wm/wm", "wm/logon", "-l", "-n", "lib/ipaqns", "-u", user}); 150 exit; 151 } 152 sys->print("init: can't load wm/logon: %r"); 153 } 154 sh := load Command Sh->PATH; 155 if(sh == nil){ 156 err(sys->sprint("can't load %s: %r", Sh->PATH)); 157 hang(); 158 } 159 spawn sh->init(nil, "sh" :: nil); 160} 161 162start(cmd: string, args: list of string) 163{ 164 disfile := cmd; 165 if(disfile[0] != '/') 166 disfile = "/dis/"+disfile+".dis"; 167 (ok, nil) := sys->stat(disfile); 168 if(ok >= 0){ 169 dis := load Command disfile; 170 if(dis == nil) 171 sys->print("init: can't load %s: %r\n", disfile); 172 else 173 spawn dis->init(nil, cmd :: args); 174 } 175} 176 177dobind(f, t: string, flags: int) 178{ 179 if(sys->bind(f, t, flags) < 0) 180 err(sys->sprint("can't bind %s on %s: %r", f, t)); 181} 182 183lightup() 184{ 185 # backlight 186 fd := sys->open("#T/ipaqctl", Sys->OWRITE); 187 if(fd != nil) 188 sys->fprint(fd, "light 1 1 0x80"); 189} 190 191# 192# Set system name from nvram if possible 193# 194setsysname(def: string) 195{ 196 v := array of byte def; 197 fd := sys->open("/nvfs/ID", sys->OREAD); 198 if(fd == nil) 199 fd = sys->open("/env/sysname", sys->OREAD); 200 if(fd != nil){ 201 buf := array[Sys->NAMEMAX] of byte; 202 nr := sys->read(fd, buf, len buf); 203 while(nr > 0 && buf[nr-1] == byte '\n') 204 nr--; 205 if(nr > 0) 206 v = buf[0:nr]; 207 } 208 fd = sys->open("/dev/sysname", sys->OWRITE); 209 if(fd != nil) 210 sys->write(fd, v, len v); 211} 212 213getclock(timefile: string, timedir: string): big 214{ 215 now := big 0; 216 if(timefile != nil){ 217 fd := sys->open(timefile, Sys->OREAD); 218 if(fd != nil){ 219 b := array[64] of byte; 220 n := sys->read(fd, b, len b-1); 221 if(n > 0){ 222 now = big string b[0:n]; 223 if(now <= big 16r20000000) 224 now = big 0; # remote itself is not initialised 225 } 226 } 227 } 228 if(now == big 0){ 229 if(timedir != nil){ 230 (ok, dir) := sys->stat(timedir); 231 if(ok < 0) { 232 sys->print("init: stat %s: %r", timedir); 233 return big 0; 234 } 235 now = big dir.atime; 236 }else{ 237 now = big 993826747000000; 238 sys->print("time warped\n"); 239 } 240 } 241 return now; 242} 243 244setclock(timefile: string, now: big) 245{ 246 fd := sys->open(timefile, sys->OWRITE); 247 if (fd == nil) { 248 sys->print("init: can't open %s: %r", timefile); 249 return; 250 } 251 252 b := sys->aprint("%ubd", now); 253 if (sys->write(fd, b, len b) != len b) 254 sys->print("init: can't write to %s: %r", timefile); 255} 256 257srv() 258{ 259 sys->print("remote debug srv..."); 260 fd := sys->open("/dev/eia0ctl", Sys->OWRITE); 261 if(fd != nil) 262 sys->fprint(fd, "b115200"); 263 264 fd = sys->open("/dev/eia0", Sys->ORDWR); 265 if (fd == nil){ 266 err(sys->sprint("can't open /dev/eia0: %r")); 267 return; 268 } 269 if (sys->export(fd, "/", Sys->EXPASYNC) < 0){ 270 err(sys->sprint("can't export on serial port: %r")); 271 return; 272 } 273} 274 275err(s: string) 276{ 277 sys->fprint(sys->fildes(2), "init: %s\n", s); 278} 279 280hang() 281{ 282 <-(chan of int); 283} 284 285tried := 0; 286 287askrootsource(localok: int, netok: int): (string, string) 288{ 289 stdin := sys->fildes(0); 290 sources := "kernel" :: nil; 291 if(netok) 292 sources = "remote" :: sources; 293 if(localok){ 294 sources = "local" :: sources; 295 if(netok) 296 sources = "local+remote" :: sources; 297 } 298Query: 299 for(;;) { 300 s := ""; 301 if (tried == 0 && (s = rdenv("rootsource", nil)) != nil) { 302 tried = 1; 303 if (s[len s - 1] == '\n') 304 s = s[:len s - 1]; 305 sys->print("/nvfs/rootsource: root from %s\n", s); 306 } else { 307 sys->print("root from ("); 308 cm := ""; 309 for(l := sources; l != nil; l = tl l){ 310 sys->print("%s%s", cm, hd l); 311 cm = ","; 312 } 313 sys->print(")[%s] ", hd sources); 314 315 s = getline(stdin, hd sources); # default 316 } 317 case s[0] { 318 Keyboard->Right or Keyboard->Left => 319 sources = append(hd sources, tl sources); 320 sys->print("\n"); 321 continue Query; 322 Keyboard->Down => 323 s = hd sources; 324 sys->print(" %s\n", s); 325 } 326 (nil, choice) := sys->tokenize(s, "\t "); 327 if(choice == nil) 328 choice = sources; 329 opt := hd choice; 330 case opt { 331 * => 332 sys->print("\ninvalid boot option: '%s'\n", opt); 333 "kernel" => 334 return (nil, "#r/rtc"); 335 "local" => 336 return ("/n/local", "#r/rtc"); 337 "local+remote" => 338 if(netfs("/n/remote") >= 0) 339 return ("/n/local", "/n/remote/dev/time"); 340 "remote" => 341 if(netfs("/n/remote") >= 0) 342 return ("/n/remote", "/n/remote/dev/time"); 343 } 344 } 345} 346 347getline(fd: ref Sys->FD, default: string): string 348{ 349 result := ""; 350 buf := array[10] of byte; 351 i := 0; 352 for(;;) { 353 n := sys->read(fd, buf[i:], len buf - i); 354 if(n < 1) 355 break; 356 i += n; 357 while(i >0 && (nutf := sys->utfbytes(buf, i)) > 0){ 358 s := string buf[0:nutf]; 359 for (j := 0; j < len s; j++) 360 case s[j] { 361 '\b' => 362 if(result != nil) 363 result = result[0:len result-1]; 364 'u'&16r1F => 365 sys->print("^U\n"); 366 result = ""; 367 '\r' => 368 ; 369 * => 370 sys->print("%c", s[j]); 371 if(s[j] == '\n' || s[j] >= 16r80){ 372 if(s[j] != '\n') 373 result[len result] = s[j]; 374 if(result == nil) 375 return default; 376 return result; 377 } 378 result[len result] = s[j]; 379 } 380 buf[0:] = buf[nutf:i]; 381 i -= nutf; 382 } 383 } 384 return default; 385} 386 387append(v: string, l: list of string): list of string 388{ 389 if(l == nil) 390 return v :: nil; 391 return hd l :: append(v, tl l); 392} 393 394# 395# serve local DOS or kfs file system using flash translation layer 396# 397lfs(): int 398{ 399 if(!flashpart("#F/flash/flashctl", flashparts)) 400 return -1; 401 if(!ftlinit("#F/flash/fs")) 402 return -1; 403 if(iskfs("#X/ftldata")) 404 return lkfs("#X/ftldata"); 405 c := chan of string; 406 spawn startfs(c, "/dis/dossrv.dis", "dossrv" :: "-f" :: "#X/ftldata" :: "-m" :: "/n/local" :: nil, nil); 407 if(<-c != nil) 408 return -1; 409 return 0; 410} 411 412wmagic := "kfs wren device\n"; 413 414iskfs(file: string): int 415{ 416 fd := sys->open(file, Sys->OREAD); 417 if(fd == nil) 418 return 0; 419 buf := array[512] of byte; 420 n := sys->read(fd, buf, len buf); 421 if(n < len buf) 422 return 0; 423 if(string buf[256:256+len wmagic] != wmagic) 424 return 0; 425 RBUFSIZE := int string buf[256+len wmagic:256+len wmagic+12]; 426 if(RBUFSIZE % 512) 427 return 0; # bad block size 428 return 1; 429} 430 431lkfs(file: string): int 432{ 433 p := array[2] of ref Sys->FD; 434 if(sys->pipe(p) < 0) 435 return -1; 436 c := chan of string; 437 spawn startfs(c, "/dis/disk/kfs.dis", "disk/kfs" :: "-A" :: "-n" :: "main" :: file :: nil, p[0]); 438 if(<-c != nil) 439 return -1; 440 p[0] = nil; 441 return sys->mount(p[1], nil, "/n/local", Sys->MREPL|Sys->MCREATE, nil); 442} 443 444startfs(c: chan of string, file: string, args: list of string, fd: ref Sys->FD) 445{ 446 if(fd != nil){ 447 sys->pctl(Sys->NEWFD, fd.fd :: 1 :: 2 :: nil); 448 sys->dup(fd.fd, 0); 449 } 450 fs := load Command file; 451 if(fs == nil){ 452 sys->print("can't load %s: %r\n", file); 453 c <-= "load failed"; 454 } 455 { 456 fs->init(nil, args); 457 c <-= nil; 458 }exception { 459 "*" => 460 c <-= "failed"; 461 * => 462 c <-= "unknown exception"; 463 } 464} 465 466# 467# partition flash 468# 469flashdone := 0; 470 471flashpart(ctl: string, parts: array of string): int 472{ 473 if(flashdone) 474 return 1; 475 cfd := sys->open(ctl, Sys->ORDWR); 476 if(cfd == nil){ 477 sys->print("can't open %s: %r\n", ctl); 478 return 0; 479 } 480 for(i := 0; i < len parts; i++) 481 if(sys->fprint(cfd, "%s", parts[i]) < 0){ 482 sys->print("can't %q to %s: %r\n", parts[i], ctl); 483 return 0; 484 } 485 flashdone = 1; 486 return 1; 487} 488 489# 490# set up flash translation layer 491# 492ftldone := 0; 493 494ftlinit(flashmem: string): int 495{ 496 if(ftldone) 497 return 1; 498 sys->print("Set flash translation of %s...\n", flashmem); 499 fd := sys->open("#X/ftlctl", Sys->OWRITE); 500 if(fd == nil){ 501 sys->print("can't open #X/ftlctl: %r\n"); 502 return 0; 503 } 504 if(sys->fprint(fd, "init %s", flashmem) <= 0){ 505 sys->print("can't init flash translation: %r\n"); 506 return 0; 507 } 508 ftldone = 1; 509 return 1; 510} 511 512configether() 513{ 514 if(ethername == nil) 515 return; 516 fd := sys->open("/nvfs/etherparams", Sys->OREAD); 517 if(fd == nil) 518 return; 519 ctl := sys->open("/net/"+ethername+"/clone", Sys->OWRITE); 520 if(ctl == nil){ 521 sys->print("init: can't open %s's clone: %r\n", ethername); 522 return; 523 } 524 b := array[1024] of byte; 525 n := sys->read(fd, b, len b); 526 if(n <= 0) 527 return; 528 for(i := 0; i < n;){ 529 for(e := i; e < n && b[e] != byte '\n'; e++) 530 ; 531 s := string b[i:e]; 532 if(sys->fprint(ctl, "%s", s) < 0) 533 sys->print("init: ctl write to %s: %s: %r\n", ethername, s); 534 i = e+1; 535 } 536} 537 538donebind := 0; 539 540# 541# set up network mount 542# 543netfs(mountpt: string): int 544{ 545 fd: ref Sys->FD; 546 if(!donebind){ 547 fd = sys->open("/net/ipifc/clone", sys->OWRITE); 548 if(fd == nil) { 549 sys->print("init: open /net/ipifc/clone: %r\n"); 550 return -1; 551 } 552 if(sys->fprint(fd, "bind ether %s", ethername) < 0) { 553 sys->print("could not bind ether0 interface: %r\n"); 554 return -1; 555 } 556 donebind = 1; 557 }else{ 558 fd = sys->open("/net/ipifc/0/ctl", Sys->OWRITE); 559 if(fd == nil){ 560 sys->print("init: can't reopen /net/ipifc/0/ctl: %r\n"); 561 return -1; 562 } 563 } 564 server := rdenv("fsip", nil); 565 if((ip := rdenv("ip", nil)) != nil) { 566 sys->print("**using %s\n", ip); 567 sys->fprint(fd, "bind ether /net/ether0"); 568 sys->fprint(fd, "add %s ", ip); 569 if((ipgw := rdenv("ipgw", nil)) != nil){ 570 rfd := sys->open("/net/iproute", Sys->OWRITE); 571 if(rfd != nil){ 572 sys->fprint(rfd, "add 0 0 %s", ipgw); 573 sys->print("**using ipgw=%s\n", ipgw); 574 } 575 } 576 }else if(server == nil){ 577 sys->print("dhcp..."); 578 dhcpclient = load Dhcpclient Dhcpclient->PATH; 579 if(dhcpclient == nil){ 580 sys->print("can't load dhcpclient: %r\n"); 581 return -1; 582 } 583 dhcpclient->init(); 584 (cfg, nil, e) := dhcpclient->dhcp("/net", fd, "/net/ether0/addr", nil, nil); 585 if(e != nil){ 586 sys->print("dhcp: %s\n", e); 587 return -1; 588 } 589 if(server == nil) 590 server = cfg.getip(Dhcpclient->OP9fs); 591 dhcpclient = nil; 592 } 593 if(server == nil || server == "0.0.0.0"){ 594 sys->print("no file server address\n"); 595 return -1; 596 } 597 sys->print("fs=%s\n", server); 598 599 net := "tcp"; # how to specify il? 600 svcname := net + "!" + server + "!6666"; 601 602 sys->print("dial %s...", svcname); 603 604 (ok, c) := sys->dial(svcname, nil); 605 if(ok < 0){ 606 sys->print("can't dial %s: %r\n", svcname); 607 return -1; 608 } 609 610 sys->print("\nConnected ...\n"); 611 if(kr != nil){ 612 err: string; 613 sys->print("Authenticate ..."); 614 ai := kr->readauthinfo("/nvfs/default"); 615 if(ai == nil){ 616 sys->print("readauthinfo /nvfs/default failed: %r\n"); 617 sys->print("trying mount as `nobody'\n"); 618 } 619 (c.dfd, err) = auth->client("none", ai, c.dfd); 620 if(c.dfd == nil){ 621 sys->print("authentication failed: %s\n", err); 622 return -1; 623 } 624 } 625 626 sys->print("mount %s...", mountpt); 627 628 c.cfd = nil; 629 n := sys->mount(c.dfd, nil, mountpt, sys->MREPL, ""); 630 if(n > 0) 631 return 0; 632 if(n < 0) 633 sys->print("%r"); 634 return -1; 635} 636 637calibrate() 638{ 639 val := rf("/nvfs/calibrate", nil); 640 if(val != nil){ 641 fd := sys->open("/dev/touchctl", Sys->OWRITE); 642 if(fd != nil && sys->fprint(fd, "%s", val) >= 0) 643 return; 644 } 645 done := chan of int; 646 spawn docal(done); 647 <-done; 648} 649 650docal(done: chan of int) 651{ 652 sys->pctl(Sys->FORKFD, nil); 653 ofd := sys->create("/nvfs/calibrate", Sys->OWRITE, 8r644); 654 if(ofd != nil) 655 sys->dup(ofd.fd, 1); 656 cal := load Command "/dis/touchcal.dis"; 657 if(cal != nil){ 658 { 659 cal->init(nil, "touchcal" :: nil); 660 }exception{ 661 "fail:*" => 662 ; 663 } 664 } 665 done <-= 1; 666} 667 668userok(user: string): int 669{ 670 (ok, d) := sys->stat("/usr/"+user); 671 return ok >= 0 && (d.mode & Sys->DMDIR) != 0; 672} 673 674rdenv(name: string, def: string): string 675{ 676 s := rf("#e/"+name, nil); 677 if(s != nil) 678 return s; 679 s = rf("/nvfs/"+name, def); 680 while(s != nil && ((c := s[len s-1]) == '\n' || c == '\r')) 681 s = s[0: len s-1]; 682 if(s != nil) 683 return s; 684 return def; 685} 686 687rf(file: string, default: string): string 688{ 689 fd := sys->open(file, Sys->OREAD); 690 if(fd != nil){ 691 buf := array[128] of byte; 692 nr := sys->read(fd, buf, len buf); 693 if(nr > 0) 694 return string buf[0:nr]; 695 } 696 return default; 697} 698