main.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "u.h"
  10. #include "../port/lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. #include "init.h"
  15. #include "io.h"
  16. #include "encoding.h"
  17. #include "ureg.h"
  18. #include <tos.h>
  19. int cpuserver = 1;
  20. extern void (*consuartputs)(char*, int);
  21. void query_mem(const char *config_string, uintptr_t *base, size_t *size);
  22. void query_rtc(const char *config_string, uintptr_t *mtime);
  23. void query_uint(const char *config_string, char *name, uintptr_t *val);
  24. void putchar(uint8_t c);
  25. void msg(char *s)
  26. {
  27. while (*s)
  28. putchar(*s++);
  29. }
  30. void die(char *s)
  31. {
  32. msg(s);
  33. while (1);
  34. }
  35. void
  36. ndnr(void)
  37. {
  38. die("ndnr");
  39. }
  40. static void puts(char * s, int n)
  41. {
  42. while (n--)
  43. putchar(*s++);
  44. }
  45. /* mach info for hart 0. */
  46. /* in many plan 9 implementations this stuff is all reserved in early assembly.
  47. * we don't have to do that. */
  48. uint64_t m0stack[4096];
  49. Mach m0;
  50. Sys asys, *sys=&asys;
  51. Conf conf;
  52. uintptr_t kseg0 = KZERO;
  53. char *cputype = "riscv";
  54. int64_t hz;
  55. uintptr_t rtc;
  56. /* I forget where this comes from and I don't care just now. */
  57. uint32_t kerndate;
  58. int maxcores = 1;
  59. int nosmp = 1;
  60. uint64_t mtimepa, mtimecmppa;
  61. uint64_t *mtime, *mtimecmp;
  62. /*
  63. * kseg2 is the base of the virtual address space.
  64. * it is not a constant as in amd64; in riscv there are many possible
  65. * values, even on the same SOC. It is determined by firmware.
  66. */
  67. void *kseg2;
  68. char *configstring; /* from coreboot, first arg to main */
  69. static uintptr_t sp; /* XXX - must go - user stack of init proc */
  70. /* general purpose hart startup. We call this via startmach.
  71. * When we enter here, the machp() function is usable.
  72. */
  73. void hart(void)
  74. {
  75. //Mach *mach = machp();
  76. die("not yet");
  77. }
  78. uint64_t
  79. rdtsc(void)
  80. {
  81. uint64_t cycles;
  82. // msg("rdtsc\n");
  83. cycles = read_csr(/*s*/cycle);
  84. //print("cycles in rdtsc is 0x%llx\n", cycles);
  85. // msg("done rdts\n");
  86. return cycles;
  87. }
  88. void
  89. loadenv(int argc, char* argv[])
  90. {
  91. char *env[2];
  92. /*
  93. * Process command line env options
  94. */
  95. while(--argc > 0){
  96. char* next = *++argv;
  97. if(next[0] !='-'){
  98. if (gettokens(next, env, 2, "=") == 2){;
  99. ksetenv(env[0], env[1], 0);
  100. }else{
  101. print("Ignoring parameter with no value: %s\n", env[0]);
  102. }
  103. }
  104. }
  105. }
  106. void
  107. init0(void)
  108. {
  109. Proc *up = externup();
  110. char buf[2*KNAMELEN];
  111. Ureg u;
  112. up->nerrlab = 0;
  113. /*
  114. * if(consuart == nil)
  115. * i8250console("0");
  116. */
  117. spllo();
  118. /*
  119. * These are o.k. because rootinit is null.
  120. * Then early kproc's will have a root and dot.
  121. */
  122. print("init0: up is %p\n", up);
  123. up->slash = namec("#/", Atodir, 0, 0);
  124. print("1\n");
  125. pathclose(up->slash->path);
  126. print("1\n");
  127. up->slash->path = newpath("/");
  128. print("1\n");
  129. up->dot = cclone(up->slash);
  130. print("1\n");
  131. devtabinit();
  132. print("1\n");
  133. if(!waserror()){
  134. //snprint(buf, sizeof(buf), "%s %s", "AMD64", conffile);
  135. //loadenv(oargc, oargv);
  136. ksetenv("terminal", buf, 0);
  137. ksetenv("cputype", cputype, 0);
  138. ksetenv("pgsz", "2097152", 0);
  139. // no longer. confsetenv();
  140. poperror();
  141. }
  142. kproc("alarm", alarmkproc, 0);
  143. //nixprepage(-1);
  144. print("TOUSER: kstack is %p\n", up->kstack);
  145. //debugtouser((void *)UTZERO);
  146. memset(&u, 0, sizeof(u));
  147. u.ip = (uintptr_t)init_main;
  148. u.sp = sp;
  149. u.a2 = USTKTOP-sizeof(Tos);
  150. touser(&u);
  151. }
  152. /*
  153. * Option arguments from the command line.
  154. * oargv[0] is the boot file.
  155. * TODO: do it.
  156. */
  157. static int64_t oargc;
  158. static char* oargv[20];
  159. static char oargb[1024];
  160. static int oargblen;
  161. void
  162. bootargs(uintptr_t base)
  163. {
  164. int i;
  165. uint32_t ssize;
  166. char **av, *p;
  167. /*
  168. * Push the boot args onto the stack.
  169. * Make sure the validaddr check in syscall won't fail
  170. * because there are fewer than the maximum number of
  171. * args by subtracting sizeof(up->arg).
  172. */
  173. i = oargblen+1;
  174. p = UINT2PTR(STACKALIGN(base + BIGPGSZ - sizeof(((Proc*)0)->arg) - i));
  175. memmove(p, oargb, i);
  176. /*
  177. * Now push argc and the argv pointers.
  178. * This isn't strictly correct as the code jumped to by
  179. * touser in init9.[cs] calls startboot (port/initcode.c) which
  180. * expects arguments
  181. * startboot(char* argv0, char* argv[])
  182. * not the usual (int argc, char* argv[]), but argv0 is
  183. * unused so it doesn't matter (at the moment...).
  184. */
  185. av = (char**)(p - (oargc+2)*sizeof(char*));
  186. ssize = base + BIGPGSZ - PTR2UINT(av);
  187. print("Stack size in boot args is %p\n", ssize);
  188. *av++ = (char*)oargc;
  189. for(i = 0; i < oargc; i++)
  190. *av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BIGPGSZ);
  191. *av = nil;
  192. sp = USTKTOP - ssize;
  193. print("New sp in bootargs is %p\n", sp);
  194. }
  195. void
  196. userinit(void)
  197. {
  198. Proc *up = externup();
  199. Proc *p;
  200. Segment *s;
  201. KMap *k;
  202. Page *pg;
  203. int sno;
  204. p = newproc();
  205. p->pgrp = newpgrp();
  206. p->egrp = smalloc(sizeof(Egrp));
  207. p->egrp->r.ref = 1;
  208. p->fgrp = dupfgrp(nil);
  209. p->rgrp = newrgrp();
  210. p->procmode = 0640;
  211. kstrdup(&eve, "");
  212. kstrdup(&p->text, "*init*");
  213. kstrdup(&p->user, eve);
  214. /*
  215. * Kernel Stack
  216. *
  217. * N.B. make sure there's enough space for syscall to check
  218. * for valid args and
  219. * space for gotolabel's return PC
  220. * AMD64 stack must be quad-aligned.
  221. */
  222. p->sched.pc = PTR2UINT(init0);
  223. p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->arg)-sizeof(uintptr_t));
  224. p->sched.sp = STACKALIGN(p->sched.sp);
  225. /*
  226. * User Stack
  227. *
  228. * Technically, newpage can't be called here because it
  229. * should only be called when in a user context as it may
  230. * try to sleep if there are no pages available, but that
  231. * shouldn't be the case here.
  232. */
  233. sno = 0;
  234. print("newseg(0x%x, %p, 0x%llx)\n", SG_STACK|SG_READ|SG_WRITE, (void *)USTKTOP-USTKSIZE, USTKSIZE/ BIGPGSZ);
  235. s = newseg(SG_STACK|SG_READ|SG_WRITE, USTKTOP-USTKSIZE, USTKSIZE/ BIGPGSZ);
  236. p->seg[sno++] = s;
  237. pg = newpage(1, 0, USTKTOP-BIGPGSZ, BIGPGSZ, -1);
  238. segpage(s, pg);
  239. k = kmap(pg);
  240. bootargs(VA(k));
  241. kunmap(k);
  242. /*
  243. * Text
  244. */
  245. s = newseg(SG_TEXT|SG_READ|SG_EXEC, UTZERO, 1);
  246. s->flushme++;
  247. p->seg[sno++] = s;
  248. pg = newpage(1, 0, UTZERO, BIGPGSZ, -1);
  249. memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
  250. segpage(s, pg);
  251. k = kmap(s->map[0]->pages[0]);
  252. /* UTZERO is only needed until we make init not have 2M block of zeros at the front. */
  253. memmove(UINT2PTR(VA(k) + init_code_start - UTZERO), init_code_out, sizeof(init_code_out));
  254. kunmap(k);
  255. /*
  256. * Data
  257. */
  258. s = newseg(SG_DATA|SG_READ|SG_WRITE, UTZERO + BIGPGSZ, 1);
  259. s->flushme++;
  260. p->seg[sno++] = s;
  261. pg = newpage(1, 0, UTZERO + BIGPGSZ, BIGPGSZ, -1);
  262. memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
  263. segpage(s, pg);
  264. k = kmap(s->map[0]->pages[0]);
  265. /* This depends on init having a text segment < 2M. */
  266. memmove(UINT2PTR(VA(k) + init_data_start - (UTZERO + BIGPGSZ)), init_data_out, sizeof(init_data_out));
  267. kunmap(k);
  268. ready(p);
  269. }
  270. void
  271. confinit(void)
  272. {
  273. int i;
  274. conf.npage = 0;
  275. for(i=0; i<nelem(conf.mem); i++)
  276. conf.npage += conf.mem[i].npage;
  277. conf.nproc = 1000;
  278. conf.nimage = 200;
  279. }
  280. /* check checks simple atomics and anything else that is critical to correct operation.
  281. * You can make the prints optional on errors cases, not have it print all tests,
  282. * but you should never remove check or the call to it. It found some nasty problems. */
  283. static void
  284. check(void)
  285. {
  286. uint64_t f2ns;
  287. // cas test
  288. uint32_t t = 0;
  289. int fail = 0;
  290. int _42 = 42;
  291. int a = _42, b;
  292. int x = cas32(&t, 0, 1);
  293. print("cas32 done x %d (want 1) t %d (want 1)\n", x, t);
  294. if ((t != 1) || (x != 1))
  295. fail++;
  296. x = cas32(&t, 0, 1);
  297. print("cas32 done x %d (want 0) t %d (want 1)\n", x, t);
  298. if ((t != 1) || (x != 0))
  299. fail++;
  300. print("t is now %d before final cas32\n", t);
  301. x = cas32(&t, 1, 2);
  302. print("cas32 done x %d (want 1) t %d (want 2)\n", x, t);
  303. if ((t != 2) || (x != 1))
  304. fail++;
  305. t = 0;
  306. x = tas32(&t);
  307. print("tas done x %d (want 0) t %d (want 1)\n", x, t);
  308. if ((t != 1) || (x != 0))
  309. fail++;
  310. x = tas32(&t);
  311. print("tas done x %d (want 1) t %d (want 1)\n", x, t);
  312. if ((t != 1) || (x != 1))
  313. fail++;
  314. t = 0;
  315. x = tas32(&t);
  316. print("tas done x %d (want ) t %d (want 1)\n", x, t);
  317. if ((t != 1) || (x != 0))
  318. fail++;
  319. b = ainc(&a);
  320. print("after ainc a is %d (want 43) b is %d (want 43)\n", a, b);
  321. if ((b != _42 + 1) || (a != _42 + 1))
  322. fail++;
  323. b = ainc(&a);
  324. print("after ainc a is %d (want 44) b is %d (want 44)\n", a, b);
  325. if ((b != _42 + 2) || (a != _42 + 2))
  326. fail++;
  327. b = adec(&a);
  328. print("after ainc a is %d (want 43) b is %d (want 43)\n", a, b);
  329. if ((b != _42 + 1) || (a != _42 + 1))
  330. fail++;
  331. if (fail) {
  332. print("%d failures in check();\n", fail);
  333. panic("FIX ME");
  334. }
  335. f2ns = fastticks2ns(10);
  336. if ((f2ns < 1) || (f2ns > 10)) {
  337. print("fastticks2ns(1) is nuts: %d\n", f2ns);
  338. panic("Should be in the range 1 to 10, realistically");
  339. }
  340. f2ns = ns2fastticks(1);
  341. if ((f2ns < 2) || (f2ns > 100)) {
  342. print("ns2fastticks(1) is nuts: %d\n", f2ns);
  343. panic("Should be in the range 2 to 100, realistically");
  344. }
  345. }
  346. void bsp(void *stack, uintptr_t _configstring)
  347. {
  348. kseg2 = findKSeg2();
  349. msg("HIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII\n");
  350. configstring = KADDR(_configstring);
  351. msg(configstring);
  352. Mach *mach = machp();
  353. if (mach != &m0)
  354. die("MACH NOT MATCH");
  355. msg("memset mach\n");
  356. memset(mach, 0, sizeof(Mach));
  357. msg("done that\n");
  358. MACHP(0) = mach;
  359. msg(configstring);
  360. mach->self = (uintptr_t)mach;
  361. msg("SET SELF OK\n");
  362. mach->machno = 0;
  363. mach->online = 1;
  364. mach->NIX.nixtype = NIXTC;
  365. mach->stack = PTR2UINT(stack);
  366. *(uintptr_t*)mach->stack = STACKGUARD;
  367. msg(configstring);
  368. mach->externup = nil;
  369. active.nonline = 1;
  370. active.exiting = 0;
  371. active.nbooting = 0;
  372. consuartputs = puts;
  373. msg("call asminit\n");
  374. msg("==============================================\n");
  375. asminit();
  376. msg(",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,\n");
  377. asmmapinit(0x81000000, 0x3f000000, 1);
  378. msg(configstring);
  379. /*
  380. * Need something for initial delays
  381. * until a timebase is worked out.
  382. */
  383. mach->cpuhz = 2000000000ll;
  384. mach->cpumhz = 2000;
  385. sys->cyclefreq = mach->cpuhz;
  386. sys->nmach = 1;
  387. msg(configstring);
  388. fmtinit();
  389. print("\nHarvey\n");
  390. print("KADDR OF (uintptr_t) 0x40001000 is %p\n", KADDR((uintptr_t) 0x40001000));
  391. /* you're going to love this. Print does not print the whole
  392. * string. msg does. Bug. */
  393. print("Config string:%p '%s'\n", configstring, configstring);
  394. msg("Config string via msg\n");
  395. msg(configstring);
  396. msg("\n");
  397. mach->perf.period = 1;
  398. if((hz = archhz()) != 0ll){
  399. mach->cpuhz = hz;
  400. mach->cyclefreq = hz;
  401. sys->cyclefreq = hz;
  402. mach->cpumhz = hz/1000000ll;
  403. }
  404. print("print a number like 5 %d\n", 5);
  405. /*
  406. * Mmuinit before meminit because it
  407. * flushes the TLB via machp()->pml4->pa.
  408. */
  409. mmuinit();
  410. ioinit(); print("ioinit\n");
  411. print("IOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIO\n");
  412. meminit();print("meminit\n");
  413. print("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n");
  414. confinit();print("confinit\n");
  415. print("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n");
  416. archinit();print("archinit\n");
  417. print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
  418. mallocinit();print("mallocinit\n");
  419. /* test malloc. It's easier to find out it's broken here,
  420. * not deep in some call chain.
  421. * See next note.
  422. *
  423. */
  424. if (1) {
  425. void *v = malloc(1234);
  426. msg("allocated\n ");
  427. free(v);
  428. msg("free ok\n");
  429. }
  430. query_rtc(configstring, &rtc);
  431. print("rtc: %p\n", rtc);
  432. query_uint(configstring, "rtc{addr", (uintptr_t*)&mtimepa);
  433. mtime = KADDR(mtimepa);
  434. query_uint(configstring, "core{0{0{timecmp", (uintptr_t*)&mtimecmppa);
  435. mtimecmp = KADDR(mtimecmppa);
  436. print("mtime is %p and mtimecmp is %p\n", mtime, mtimecmp);
  437. umeminit();
  438. procinit0();
  439. print("before mpacpi, maxcores %d\n", maxcores);
  440. trapinit();
  441. print("trapinit done\n");
  442. /* Forcing to single core if desired */
  443. if(!nosmp) {
  444. // smp startup
  445. }
  446. //working.
  447. // not needed. teardownidmap(mach);
  448. timersinit();print(" timersinit();\n");
  449. // ? fpuinit();
  450. psinit(conf.nproc);print(" psinit(conf.nproc);\n");
  451. initimage();print(" initimage();\n");
  452. links();
  453. devtabreset();print(" devtabreset();\n");
  454. pageinit();print(" pageinit();\n");
  455. swapinit();print(" swapinit();\n");
  456. userinit();print(" userinit();\n");
  457. /* Forcing to single core if desired */
  458. if(!nosmp) {
  459. //nixsquids();
  460. //testiccs();
  461. }
  462. print("NO profiling until you set upa alloc_cpu_buffers()\n");
  463. //alloc_cpu_buffers();
  464. print("CPU Freq. %dMHz\n", mach->cpumhz);
  465. // set the trap vector
  466. void *supervisor_trap_entry(void);
  467. write_csr(/*stvec*/0x105, supervisor_trap_entry);
  468. // enable all interrupt sources.
  469. uint64_t ints = read_csr(sie);
  470. ints |= 0x666;
  471. write_csr(sie, ints);
  472. dumpmmuwalk(0xfffffffffffff000ULL);
  473. check();
  474. void consread(void);
  475. addclock0link(consread, 0);
  476. print("schedinit...\n");
  477. schedinit();
  478. die("Completed hart for bsp OK!\n");
  479. }
  480. /* stubs until we implement in assembly */
  481. int corecolor(int _)
  482. {
  483. return -1;
  484. }
  485. Proc *externup(void)
  486. {
  487. if (! machp())
  488. return nil;
  489. return machp()->externup;
  490. }
  491. void errstr(char *s, int i) {
  492. panic("errstr");
  493. }
  494. void
  495. hardhalt(void)
  496. {
  497. panic((char *)__func__);
  498. }
  499. void
  500. ureg2gdb(Ureg *u, uintptr_t *g)
  501. {
  502. panic((char *)__func__);
  503. }
  504. int
  505. userureg(Ureg*u)
  506. {
  507. int64_t ip = (int64_t)u->ip;
  508. if (ip < 0) {
  509. //print("RETURNING 0 for userureg\n");
  510. return 0;
  511. }
  512. //print("Returning 1 for userureg; need a better test\n");
  513. return 1;
  514. }
  515. void exit(int _)
  516. {
  517. panic((char *)__func__);
  518. }
  519. void fpunoted(void)
  520. {
  521. print((char *)__func__);
  522. print("NOT DOING IT. IT WILL HURT LATER\n");
  523. }
  524. void fpunotify(Ureg*_)
  525. {
  526. print("fpunotify: doing nothing since FPU is disabled\n");
  527. }
  528. void fpusysrfork(Ureg*_)
  529. {
  530. print((char *)__func__);
  531. print("IGNORING\n");
  532. }
  533. void sysrforkret(void)
  534. {
  535. void *stack(void);
  536. void *sp = stack();
  537. if(0) print("sysrforkret: stack is %p\n", sp);
  538. if(0) dumpgpr((Ureg *)sp);
  539. void _sysrforkret();
  540. _sysrforkret();
  541. }
  542. void
  543. reboot(void*_, void*__, int32_t ___)
  544. {
  545. panic("reboot");
  546. }
  547. void fpusysprocsetup(Proc *_)
  548. {
  549. print((char *)__func__);
  550. print("THIS IS GONNA SCREW YOU IF YOU DO NOT FIX IT\n");
  551. }
  552. void fpusysrforkchild(Proc*_, Proc*__)
  553. {
  554. print((char *)__func__);
  555. print("THIS IS GONNA SCREW YOU IF YOU DO NOT FIX IT\n");
  556. }
  557. int
  558. fpudevprocio(Proc*p, void*v, int32_t _, uintptr_t __, int ___)
  559. {
  560. panic((char *)__func__);
  561. return -1;
  562. }
  563. void cycles(uint64_t *p)
  564. {
  565. *p = rdtsc();
  566. }
  567. int islo(void)
  568. {
  569. // msg("isloc\n");
  570. uint64_t ms = read_csr(sstatus);
  571. // msg("read it\n");
  572. return ms & MSTATUS_SIE;
  573. }
  574. void
  575. stacksnippet(void)
  576. {
  577. //Stackframe *stkfr;
  578. kmprint(" stack:");
  579. // for(stkfr = stackframe(); stkfr != nil; stkfr = stkfr->next)
  580. // kmprint(" %c:%p", ktextaddr(stkfr->pc) ? 'k' : '?', ktextaddr(stkfr->pc) ? (stkfr->pc & 0xfffffff) : stkfr->pc);
  581. kmprint("\n");
  582. }
  583. /* crap. */
  584. /* this should come from build but it's intimately tied in to VGA. Crap. */
  585. Physseg physseg[8];
  586. int nphysseg = 8;
  587. /* bringup -- remove asap. */
  588. void
  589. DONE(void)
  590. {
  591. print("DONE\n");
  592. //prflush();
  593. delay(10000);
  594. ndnr();
  595. }
  596. void
  597. HERE(void)
  598. {
  599. print("here\n");
  600. //prflush();
  601. delay(5000);
  602. }
  603. /* The old plan 9 standby ... wave ... */
  604. /* Keep to debug trap.c */
  605. void wave(int c)
  606. {
  607. putchar(c);
  608. }
  609. void hi(char *s)
  610. {
  611. if (! s)
  612. s = "<NULL>";
  613. while (*s)
  614. wave(*s++);
  615. }